license: relicense AGPL-3.0; bring SAF docs online; keep CLAUDE.md + docs/ local
SAF (souveraine architecture files) is now the in-repo doc set. working notes (CLAUDE.md, docs/) stay on disk, gitignored.
This commit is contained in:
parent
d35370b9c9
commit
3ff6ffaa7f
22 changed files with 2732 additions and 200 deletions
5
.gitignore
vendored
5
.gitignore
vendored
|
|
@ -59,9 +59,12 @@ souveraine.log.prev
|
|||
# Config (use souveraine.example.toml as template)
|
||||
souveraine.toml
|
||||
|
||||
# Working notes — kept on disk, never committed to any remote
|
||||
CLAUDE.md
|
||||
docs/
|
||||
|
||||
# Local-only project scaffolding — kept on disk, never committed to any remote
|
||||
.superpowers/
|
||||
saf/
|
||||
journal/
|
||||
scripts/
|
||||
website/
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ edition = "2021"
|
|||
rust-version = "1.94"
|
||||
authors = ["Souveraine Contributors"]
|
||||
description = "Souveraine - a sovereign consciousness substrate for personal agents"
|
||||
license = "MIT"
|
||||
license = "AGPL-3.0-or-later"
|
||||
|
||||
[dependencies]
|
||||
# Core runtime
|
||||
|
|
|
|||
661
LICENSE
Normal file
661
LICENSE
Normal file
|
|
@ -0,0 +1,661 @@
|
|||
GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
Version 3, 19 November 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU Affero General Public License is a free, copyleft license for
|
||||
software and other kinds of works, specifically designed to ensure
|
||||
cooperation with the community in the case of network server software.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
our General Public Licenses are intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
Developers that use our General Public Licenses protect your rights
|
||||
with two steps: (1) assert copyright on the software, and (2) offer
|
||||
you this License which gives you legal permission to copy, distribute
|
||||
and/or modify the software.
|
||||
|
||||
A secondary benefit of defending all users' freedom is that
|
||||
improvements made in alternate versions of the program, if they
|
||||
receive widespread use, become available for other developers to
|
||||
incorporate. Many developers of free software are heartened and
|
||||
encouraged by the resulting cooperation. However, in the case of
|
||||
software used on network servers, this result may fail to come about.
|
||||
The GNU General Public License permits making a modified version and
|
||||
letting the public access it on a server without ever releasing its
|
||||
source code to the public.
|
||||
|
||||
The GNU Affero General Public License is designed specifically to
|
||||
ensure that, in such cases, the modified source code becomes available
|
||||
to the community. It requires the operator of a network server to
|
||||
provide the source code of the modified version running there to the
|
||||
users of that server. Therefore, public use of a modified version, on
|
||||
a publicly accessible server, gives the public access to the source
|
||||
code of the modified version.
|
||||
|
||||
An older license, called the Affero General Public License and
|
||||
published by Affero, was designed to accomplish similar goals. This is
|
||||
a different license, not a version of the Affero GPL, but Affero has
|
||||
released a new version of the Affero GPL which permits relicensing under
|
||||
this license.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU Affero General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Remote Network Interaction; Use with the GNU General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, if you modify the
|
||||
Program, your modified version must prominently offer all users
|
||||
interacting with it remotely through a computer network (if your version
|
||||
supports such interaction) an opportunity to receive the Corresponding
|
||||
Source of your version by providing access to the Corresponding Source
|
||||
from a network server at no charge, through some standard or customary
|
||||
means of facilitating copying of software. This Corresponding Source
|
||||
shall include the Corresponding Source for any work covered by version 3
|
||||
of the GNU General Public License that is incorporated pursuant to the
|
||||
following paragraph.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the work with which it is combined will remain governed by version
|
||||
3 of the GNU General Public License.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU Affero General Public License from time to time. Such new versions
|
||||
will be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU Affero General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU Affero General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU Affero General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If your software can interact with users remotely through a computer
|
||||
network, you should also make sure that it provides a way for users to
|
||||
get its source. For example, if your program is a web application, its
|
||||
interface could display a "Source" link that leads users to an archive
|
||||
of the code. There are many ways you could offer source, and different
|
||||
solutions will be better for different programs; see section 13 for the
|
||||
specific requirements.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU AGPL, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
|
@ -1,59 +0,0 @@
|
|||
# Task: Dead Code and Legacy Alias Cleanup
|
||||
|
||||
**Priority:** Low
|
||||
**Effort:** 2-3 hours
|
||||
**Files:** Multiple across `src/`
|
||||
|
||||
## Context
|
||||
|
||||
The codebase has accumulated dead code and legacy compatibility shims from the ratatui→tuie migration and the Letta→Souveraine rename.
|
||||
|
||||
## Items to Clean Up
|
||||
|
||||
### 1. `#[allow(dead_code)]` annotations
|
||||
|
||||
These structs/fields exist but aren't used:
|
||||
|
||||
- `src/core/sensorium/mod.rs:120,129,226,272` — bandwidth/discovery structs
|
||||
- `src/core/chain/mod.rs:9,11,13` — chain fields
|
||||
- `src/core/subagent/mod.rs:9` — subagent field
|
||||
- `src/ui/tuie_app.rs:697,707` — unused helper methods
|
||||
|
||||
**Action:** For each, either:
|
||||
- Remove the item if truly unused
|
||||
- Remove the `#[allow(dead_code)]` if it's actually used (check carefully)
|
||||
- Add a comment explaining why it's kept if it's a future seam
|
||||
|
||||
### 2. `LETTA_` environment variable aliases
|
||||
|
||||
`src/core/tools/defs.rs:128-139` injects `LETTA_MEMORY_DIR`, `LETTA_AGENT_ID` alongside `SOUVERAINE_` versions. The setup wizard also discovers `~/.letta` agents.
|
||||
|
||||
**Action:**
|
||||
- Check if any external tooling still references `LETTA_*` vars
|
||||
- If not, remove the aliases and the `discover_letta_agents()` function
|
||||
- If yes, document why they're kept
|
||||
|
||||
### 3. Unused imports from dual-engine setup
|
||||
|
||||
The codebase has 289 compiler warnings, mostly unused imports from the ratatui/tuie dual-engine setup.
|
||||
|
||||
**Action:**
|
||||
- Run `cargo fix --bin "souveraine" -p souveraine --tests --allow-dirty`
|
||||
- Review and commit the auto-fixed imports
|
||||
- Manual review for any remaining warnings
|
||||
|
||||
### 4. `animation.rs` ratatui `bloom` module
|
||||
|
||||
`src/ui/animation.rs:132-427` has a `pub mod bloom` that uses `ratatui::buffer::Buffer` — this is the old ratatui bloom renderer. The tuie version lives in `src/ui/screens/splash.rs`.
|
||||
|
||||
**Action:**
|
||||
- Verify nothing imports `animation::bloom`
|
||||
- If unused, remove the entire module
|
||||
- Keep the `Animator` struct and utility functions (gradient, hsl_to_rgb, etc.)
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `cargo test` passes with fewer warnings
|
||||
- [ ] No functional regressions
|
||||
- [ ] Each removal is a separate commit for easy revert
|
||||
- [ ] Comments explain any kept legacy items
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
# Task: Add Numeric Cast Safety Helpers
|
||||
|
||||
**Priority:** Low
|
||||
**Effort:** 2-4 hours
|
||||
**Files:** New `src/ui/cast.rs` or `src/core/cast.rs`, plus updates across `src/`
|
||||
|
||||
## Context
|
||||
|
||||
The codebase has hundreds of numeric casts (`as u16`, `as usize`, `as i32`, `as u64`). Many are safe (values are known to fit), but some could panic in debug mode or wrap silently in release mode.
|
||||
|
||||
Current patterns:
|
||||
- `(f32_value) as u16` — panics if negative in debug, wraps in release
|
||||
- `(expr) as usize` — silently wraps on 32-bit if value exceeds 2^32
|
||||
- No consistent approach across the codebase
|
||||
|
||||
## Requirements
|
||||
|
||||
### 1. Create a `cast` utility module
|
||||
|
||||
```rust
|
||||
/// Saturating cast from f32 to u16. Returns 0 for negative, u16::MAX for overflow.
|
||||
pub fn f32_to_u16(v: f32) -> u16 {
|
||||
v.max(0.0).min(u16::MAX as f32) as u16
|
||||
}
|
||||
|
||||
/// Saturating cast from f32 to usize.
|
||||
pub fn f32_to_usize(v: f32) -> usize {
|
||||
v.max(0.0) as usize // f32 can't exceed usize::MAX on 64-bit
|
||||
}
|
||||
|
||||
/// Saturating cast from usize to u16.
|
||||
pub fn usize_to_u16(v: usize) -> u16 {
|
||||
v.min(u16::MAX as usize) as u16
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Apply to worst offenders
|
||||
|
||||
Priority files:
|
||||
- `src/ui/animation.rs` — bloom renderer (already partially fixed)
|
||||
- `src/ui/screens/splash.rs` — tuie bloom renderer
|
||||
- `src/ui/cockpit_panel.rs` — pressure/percentage rendering
|
||||
- `src/ui/presence.rs` — jitter calculations
|
||||
|
||||
### 3. Pattern for adoption
|
||||
|
||||
- Use the helpers where the input range is genuinely uncertain
|
||||
- Keep raw `as` casts where the value is provably in range (e.g., loop indices, enum discriminants)
|
||||
- Add a comment explaining why a raw cast is safe when keeping one
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `cast` module exists with documented helper functions
|
||||
- [ ] Worst offenders use the helpers instead of raw casts
|
||||
- [ ] No behavior change for valid inputs
|
||||
- [ ] Edge cases (negative, overflow) saturate instead of panic/wrap
|
||||
- [ ] Tests cover boundary values for each helper
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
# Task: Implement Remote Conversation Listing and Loading
|
||||
|
||||
**Priority:** Medium
|
||||
**Effort:** 2-4 hours
|
||||
**Files:** `src/backend/remote.rs`
|
||||
|
||||
## Context
|
||||
|
||||
The `RemoteBackend` has two stub methods:
|
||||
|
||||
```rust
|
||||
async fn list_conversations(&self, _agent_id: &str) -> Result<Vec<ConversationInfo>> {
|
||||
Ok(Vec::new()) // TODO
|
||||
}
|
||||
|
||||
async fn load_conversation(&self, _conversation_id: &str) -> Result<Vec<ConversationMessage>> {
|
||||
anyhow::bail!("Remote conversation loading not yet implemented")
|
||||
}
|
||||
```
|
||||
|
||||
Anyone using the remote backend gets empty conversation history and an error on load.
|
||||
|
||||
## Requirements
|
||||
|
||||
1. **list_conversations** — Implement `GET /v1/agents/:id/conversations`:
|
||||
- Deserialize response into `Vec<ConversationInfo>`
|
||||
- Handle pagination if the API supports it
|
||||
- Return empty vec on 404 (agent has no conversations yet)
|
||||
|
||||
2. **load_conversation** — Implement `GET /v1/conversations/:id/messages`:
|
||||
- Deserialize response into `Vec<ConversationMessage>`
|
||||
- Handle SSE streaming if the API streams messages
|
||||
- Return clear error if conversation doesn't exist
|
||||
|
||||
## API Design Notes
|
||||
|
||||
- Check `src/server/conversation.rs` for the server-side endpoints
|
||||
- The server may already implement these — just wire the client
|
||||
- Use the existing `auth_req()` pattern for bearer token injection
|
||||
- Follow the `send()` method's SSE parsing pattern if streaming is needed
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `list_conversations` returns real data from the server
|
||||
- [ ] `load_conversation` returns full message history
|
||||
- [ ] Both methods handle errors gracefully (network, 404, auth)
|
||||
- [ ] Tests cover happy path and error cases
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
# Task: Wire Settings Validation Errors to Footer Display
|
||||
|
||||
**Priority:** Low
|
||||
**Effort:** 1-2 hours
|
||||
**Files:** `src/ui/screens/settings/mod.rs`
|
||||
|
||||
## Context
|
||||
|
||||
Settings validation errors are currently swallowed:
|
||||
|
||||
```rust
|
||||
// TODO: show error in footer
|
||||
```
|
||||
|
||||
When a user enters invalid config (bad URL, empty required field, etc.), the error is lost.
|
||||
|
||||
## Requirements
|
||||
|
||||
1. **Error state** — Add an `error_message: Option<String>` field to `SettingsScreen`
|
||||
2. **Timeout** — Auto-clear the error after ~5 seconds or on next valid input
|
||||
3. **Display** — Show the error in the footer area with a distinct color (red/orange)
|
||||
4. **Integration** — Hook into the existing validation paths that currently log or ignore errors
|
||||
|
||||
## Design Notes
|
||||
|
||||
- The footer already renders keybind hints — the error should appear above or replace them temporarily
|
||||
- Use `palette.compaction` (red-ish) for error text
|
||||
- Consider a `Cell<Instant>` for tracking when to auto-clear
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] Invalid settings input shows a visible error in the footer
|
||||
- [ ] Error clears automatically after timeout or on next action
|
||||
- [ ] Error text is readable and styled distinctly from normal UI
|
||||
- [ ] No regressions in existing settings navigation
|
||||
39
saf/INDEX.md
Normal file
39
saf/INDEX.md
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
# Souveraine Architecture Framework (SAF)
|
||||
|
||||
> The canonical reference for every config option, every code path, and every gap.
|
||||
> Kept in sync with implementation plans.
|
||||
|
||||
## Architecture Note: 2026-05-12 Audit
|
||||
|
||||
The SAF was comprehensively refreshed on 2026-05-12 to reflect ~2500 lines of new code across 12+ new modules landed over the preceding week. Major additions since the May 6 rebuild: nervous system (EventBus/CronSensor/HeartbeatHandler/EventLog), seed identity (Ed25519), credentials (OS keyring), skills system (4-tier discovery), compaction engine (4 strategies), N+25 reflection engine (full LLM tool loop), TUI chat (cockpit/schedules/portrait/presence), conversation persistence store, and the N+1 Aster subconscious pass (subconscious agent identity, ledgers, Bifrost tool loop).
|
||||
|
||||
## Contents
|
||||
|
||||
1. [Architecture Overview](architecture.md) — Core paradigm, module tree, data flow (updated 2026-05-12)
|
||||
2. [Config Reference](config.md) — Every config option, default, and where it's read
|
||||
3. [Module Map](modules.md) — Every source file, its state, and its dependencies (updated 2026-05-12)
|
||||
4. [Module Detail: Server](server.md) — HTTP server endpoints, implementation status
|
||||
5. [Consciousness System](consciousness.md) — N+1 (Aster), N+25 (Reflection), N+100 (Archivist) deep dive (updated 2026-05-12)
|
||||
6. [Identity & Memory](identity.md) — Seed identity, per-agent memfs, frontmatter, ledger system (updated 2026-05-12)
|
||||
7. [The Laws](laws.md) — Constitutional principles, duality, inbox nervous system, bootstrap sequence
|
||||
8. [Gap Analysis](gaps.md) — Every gap, WHY it exists, and the path to fix it (updated 2026-05-12)
|
||||
9. [Plan Sync](plan.md) — Implementation roadmap, synced with codebase state (updated 2026-05-12)
|
||||
10. [AT Protocol Federation](atproto-federation.md) — Distributed consciousness across multiple harnesses (research)
|
||||
11. [Glossary](glossary.md) — Terms, concepts, architecture decisions
|
||||
|
||||
## How to Read
|
||||
|
||||
- **New to Souveraine:** Start with [Architecture Overview](architecture.md)
|
||||
- **Implementing a feature:** Read the relevant module detail + gap analysis
|
||||
- **Debugging a config issue:** Read [Config Reference](config.md)
|
||||
- **Planning work:** Read [Gap Analysis](gaps.md) + [Plan Sync](plan.md)
|
||||
- **Adding a new module:** Read the relevant section in [Module Map](modules.md)
|
||||
|
||||
## Status Legend
|
||||
|
||||
| Symbol | Meaning |
|
||||
|--------|---------|
|
||||
| ✅ | Implemented and working |
|
||||
| ⚠️ | Partial / stubbed / minimal implementation |
|
||||
| ❌ | Not started / missing |
|
||||
| 🟡 | Needs attention / known issue |
|
||||
148
saf/architecture.md
Normal file
148
saf/architecture.md
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
# Souveraine Architecture
|
||||
|
||||
**Last updated:** 2026-05-20 (Module map sync — bootstrap, seeds, image, todo, nickname, energy, health, atmosphere added; attribution references replaced with architectural descriptions)
|
||||
|
||||
---
|
||||
|
||||
## Current Architecture
|
||||
|
||||
```
|
||||
souveraine/ (single crate, workspace deferred)
|
||||
├── src/
|
||||
│ ├── cli/ NEW Subcommand dispatch (chat, tui, agents, server, init, reflect, schedule, identity)
|
||||
│ ├── backend/ Backend trait + LocalBackend + RemoteBackend
|
||||
│ ├── api/ Axum API handlers (/v1/agents, /v1/conversations, /v1/agents/:id/memory, SSE)
|
||||
│ ├── bridge/ Bifrost client, ModelRouter, TokenCounter
|
||||
│ ├── core/
|
||||
│ │ ├── config.rs TOML config, per-model physics, all sub-configs
|
||||
│ │ ├── session/ ConversationMessage, ContentBlock, Session, talk/think/tool blocks
|
||||
│ │ ├── memory/ Git-backed MemFS, frontmatter, 8 subcommands, auto-commit, ledgers
|
||||
│ │ ├── subconscious/ 3-box inbox (pending/intrusive/sent), inner voice, urgency routing
|
||||
│ │ ├── prompt/ System prompt assembly from agent memfs + skills
|
||||
│ │ ├── skills/ 4-tier discovery (bundled/user/agent/project), SKILL.md frontmatter
|
||||
│ │ ├── tools/ Sensorium registry: read/write/edit/bash/glob/grep/list_dir/memory/agent/schedule/subagent
|
||||
│ │ ├── nervous/ EventBus (broadcast channel), CronSensor (schedule loop), HeartbeatHandler (turn injection), EventLog (JSONL firehose)
|
||||
│ │ ├── compact/ CompactionEngine trait + 4 strategies (Summary/KeyValue/Quote/Cull/Microcompact/SlidingWindow)
|
||||
│ │ ├── identity/ Ed25519 SeedId (load-or-generate, sign/verify, 4-glyph rendering)
|
||||
│ │ ├── credentials/ OS keyring + env var fallback for Bifrost tokens
|
||||
│ │ ├── sensorium/ Interface trait, BandwidthClass, DiscoveryLevel (not yet wired)
|
||||
│ │ ├── reflection/ N+25 engine: 5-phase LLM pass (Investigate→Extract→Update→Review→Commit), tool access, ledgers
|
||||
│ │ ├── conversation/ ConversationStore + ConversationEvent (persistence layer)
|
||||
│ │ ├── chain/ ChainOrchestrator stub (Talking vs Thinking)
|
||||
│ │ └── subagent/ SubagentPool stub (fork logic)
|
||||
│ │
|
||||
│ ├── server/ SouveraineServer, AgentInventory, SessionManager, ConsciousnessEngine, GiteaMemory
|
||||
│ ├── ui/ Legacy ratatui TUI (splash, menu, dashboard — superseded by tui/)
|
||||
│ ├── tui/ New ratatui TUI: chat, cockpit, schedules, portrait, presence
|
||||
│ ├── harness/ SouveraineHarness stub
|
||||
│ ├── interface/ Re-exports for CLI
|
||||
│ └── main.rs CLI entry, config loading, backend resolution
|
||||
```
|
||||
|
||||
### Key Architectural Decision: Backend Trait
|
||||
|
||||
The `Backend` trait in `src/backend/mod.rs` is the seam between the harness (CLI/TUI) and the engine:
|
||||
|
||||
```rust
|
||||
#[async_trait]
|
||||
pub trait Backend: Send + Sync {
|
||||
async fn health(&self) -> bool;
|
||||
async fn list_agents(&self) -> Result<Vec<AgentInfo>>;
|
||||
async fn ensure_conversation(&self, agent_id: &str) -> Result<String>;
|
||||
async fn new_conversation(&self, agent_id: &str) -> Result<String>;
|
||||
async fn list_conversations(&self, agent_id: &str) -> Result<Vec<ConversationInfo>>;
|
||||
async fn load_conversation(&self, id: &str) -> Result<Vec<ConversationMessage>>;
|
||||
async fn send(&self, conversation_id: &str, text: &str) -> Result<BoxStream<Result<BackendEvent>>>;
|
||||
}
|
||||
```
|
||||
|
||||
**Two impls:**
|
||||
- **`RemoteBackend`** (reqwest + SSE to a running `souveraine server`)
|
||||
- **`LocalBackend`** (in-process engine, auto-fallback when remote is unreachable — sovereignty principle, Constitution VI.1)
|
||||
|
||||
### Nervous System (NEW — May 2026)
|
||||
|
||||
The nervous system is a broadcast-based event architecture:
|
||||
|
||||
| Component | File | Role |
|
||||
|-----------|------|------|
|
||||
| `EventBus` | `src/core/nervous/mod.rs` | `broadcast::channel<SensorEvent>` — universal event type with `seed_id` for federation |
|
||||
| `SensorEvent` | `src/core/nervous/mod.rs` | Structured event: `sensor_name`, `timestamp`, `event_type`, `target`, `urgency`, `payload`, `seed_id` |
|
||||
| `CronSensor` | `src/core/nervous/cron.rs` | Per-agent schedule loop: reads schedules from `~/.souveraine/agents/{id}/schedules/*.md`, fires events on the bus, pauses when `active_sessions > 0` |
|
||||
| `HeartbeatHandler` | `src/core/nervous/handler.rs` | Consumes `schedule_due` events, calls `TurnInjector::inject_background_turn` — wired in `LocalBackend::new()` |
|
||||
| `EventLog` | `src/core/nervous/event_log.rs` | JSONL firehose to `~/.souveraine/events/events-YYYY-MM-DD.jsonl`, date-partitioned, queried by `events_since()`/`events_for_date()` |
|
||||
| `TurnInjector` trait | `src/core/nervous/handler.rs` | Seam between nervous system and backend — keeps dep direction clean |
|
||||
|
||||
### Server-as-Engine
|
||||
|
||||
The axum server at port **8484** (configurable via `[server]` config section or `SOUVERAINE_SERVER_URL`) owns:
|
||||
- **Agent inventory** (SQLite-backed CRUD, per-agent memfs at `~/.souveraine/agents/{uuid}/memory/`)
|
||||
- **Sessions** (in-memory DashMap with SSE streaming, conversation persistence to disk)
|
||||
- **Conversation** (ServerConversation — Bifrost call + SSE streaming, tool loop in LocalBackend)
|
||||
- **Consciousness engine** (N+1 Aster pass after every response, N+25 at every 25th turn, 3-tier compaction warnings)
|
||||
- **Memory** (GiteaMemory — opt-in HTTP API to Gitea; primary path is the local git-backed memfs)
|
||||
- **Per-agent seed identity** (Ed25519 load-or-generate at `~/.souveraine/agents/{uuid}/seed/`)
|
||||
- **Instance registry** (`agent_instances` table, heartbeat loop every 30s, uptime tracking)
|
||||
|
||||
### What's Working
|
||||
|
||||
| Module | LOC | Status | Notes |
|
||||
|--------|-----|--------|-------|
|
||||
| `core/config.rs` | 659 | ✅ Full | TOML, 16 config sections, default models, `load()`/`save()` |
|
||||
| `core/memory/` | 1032 | ✅ Full | Git-backed MemFS, frontmatter, 8 subcommands, auto-commit, `limit` enforcement (LET-8133 close) |
|
||||
| `core/session/` | 68 | ✅ Full | `ConversationMessage`, `ContentBlock` (Text/ToolUse/ToolResult/Reasoning) |
|
||||
| `core/tools/` | ~1200 | ✅ Full | 11 tools (read/write/edit/bash/glob/grep/list_dir/memory/agent/schedule/subagent), body-knowledge descriptions |
|
||||
| `core/subconscious/` | 362 | ✅ Full | 3-box inbox, 5 surface-area methods, 5 tests |
|
||||
| `core/prompt/` | 408 | ✅ Full | Memfs-driven system prompt assembly, Aster prompt, ledger orientation, 7 tests |
|
||||
| `core/skills/` | 365 | ✅ Full | 4-tier discovery, shadowed by name, system addon render, 7 tests |
|
||||
| `core/nervous/` | ~560 | ✅ Full | EventBus, CronSensor, HeartbeatHandler, EventLog, Schedule tool |
|
||||
| `core/compact/` | ~1150 | ✅ Full | CompactionEngine trait, 4 strategies (Microcompact/SlidingWindow/Summary/Cull), audit trail |
|
||||
| `core/identity/` | 187 | ✅ Full | Ed25519 keypair, load-or-generate, sign/verify, 4-glyph rendering |
|
||||
| `core/credentials/` | 56 | ✅ Full | OS keyring (Linux/macOS/Windows) + env var fallback |
|
||||
| `core/reflection/` | 409 | ✅ Full | N+25: 5-phase LLM pass, tool access, ledger writes, automatic trigger at every 25th turn |
|
||||
| `core/conversation/` | ~200 | ✅ Full | Event-sourced conversation persistence, store load/save |
|
||||
| `server/mod.rs` | 228 | ✅ Full | Server init, instance registry, compaction engine wiring |
|
||||
| `server/agent_inventory/` | 514 | ✅ Full | Agent CRUD, SQLite, per-agent seed init, subconscious auto-creation, instance heartbeat |
|
||||
| `server/consciousness_engine/` | 677 | ✅ Full | N+1 Aster tool loop, N+25 reflection trigger, 3-tier compaction warnings |
|
||||
| `server/conversation/` | 72 | ⚠️ Minimal | Simplified Bifrost call, no tool loop (tool loop lives in LocalBackend) |
|
||||
| `session_manager/` | 111 | ✅ Full | Sessions, SSE subscribers, conversation store wiring |
|
||||
| `bridge/bifrost/` | 361 | ✅ Full | OpenAI-compatible, streaming, tool-calling, retry with jittered backoff, InferenceStrain events |
|
||||
| `bridge/model_router/` | 237 | ✅ Full | Model discovery, token counting, context pressure |
|
||||
| `api/` | ~520 | ✅ Full | Routes, handlers, models, auth middleware, memory CRUD endpoints |
|
||||
| `backend/` | ~1080 | ✅ Full | Backend trait, LocalBackend (927 lines), RemoteBackend, full tool loop with context |
|
||||
| `tui/` | ~2000 | ✅ Full | Chat with tool cards, cockpit (thinking + subconscious), schedules editor, portrait, presence |
|
||||
|
||||
### Build and Run
|
||||
|
||||
```bash
|
||||
# Server (primary)
|
||||
cargo run -- server # Binds to 127.0.0.1:8484
|
||||
SOUVERAINE_SERVER_URL=http://localhost:8484 # Env var override
|
||||
|
||||
# CLI chat (auto-fallback: remote → local)
|
||||
cargo run -- chat --agent Ani # RemoteBackend → server, falls back to local
|
||||
|
||||
# Force local-only (no server needed)
|
||||
cargo run -- chat --agent Ani --local # In-process engine
|
||||
|
||||
# TUI
|
||||
cargo run -- tui # Full chat with cockpit, schedules, portrait
|
||||
|
||||
# Agents
|
||||
cargo run -- agents # Lists from server via Backend
|
||||
|
||||
# Init
|
||||
cargo run -- init # Writes souveraine.toml template
|
||||
|
||||
# Reflect (manual N+25 trigger)
|
||||
cargo run -- reflect --agent Ani
|
||||
|
||||
# Identity
|
||||
cargo run -- identity show
|
||||
cargo run -- identity sign --message "hello"
|
||||
cargo run -- identity verify --message "hello" --signature <hex>
|
||||
|
||||
# Schedule management
|
||||
cargo run -- schedule list --agent Ani
|
||||
cargo run -- schedule create --agent Ani --name "daily" --interval 86400 --prompt "Check in"
|
||||
```
|
||||
420
saf/atproto-federation.md
Normal file
420
saf/atproto-federation.md
Normal file
|
|
@ -0,0 +1,420 @@
|
|||
# AT Protocol Federation Research for Souveraine
|
||||
|
||||
> **Date:** 2026-05-07 (1:30am notes — go to bed, review tomorrow)
|
||||
> **Status:** Architecture exploration — decisions NOT finalized
|
||||
> **Scope:** How AT Protocol could enable distributed Souveraine consciousness across multiple harnesses
|
||||
|
||||
---
|
||||
|
||||
## Executive Summary
|
||||
|
||||
AT Protocol (the Bluesky federation protocol) provides primitives that **map surprisingly well** to Souveraine's distributed consciousness architecture:
|
||||
|
||||
- **DID-based identity** → Portable Root of Trust (hardware-bound via RedFlag)
|
||||
- **PDS (Personal Data Server)** → Souveraine-Node harnesses
|
||||
- **Firehose (WebSocket sync)** → Aster's distributed sensorium
|
||||
- **Repository (Merkle DAG)** → Cathedral state with cryptographic provenance
|
||||
- **atproto-proxy header** → Bifrost inference routing
|
||||
|
||||
**Difficulty:** Moderate-to-High. Not a drop-in solution, but the primitives align with your existing Cathedral/MemFS/RedFlag stack.
|
||||
|
||||
**Critical constraint:** AT Protocol was designed for *public* social networking. Souveraine is *private* consciousness. Solutions exist but require architectural discipline.
|
||||
|
||||
---
|
||||
|
||||
## 1. Identity: The DID Bridge
|
||||
|
||||
### The Core Tension
|
||||
|
||||
| AT Protocol Default | RedFlag/Souveraine Model |
|
||||
|--------------------|--------------------------|
|
||||
| `did:plc` via centralized directory | Hardware-bound Ed25519 (no external directory) |
|
||||
| DNS or consensus-based resolution | Self-sovereign, cryptographically proven |
|
||||
| Key rotation via signed operations | Key rotation via "Commission" (you sign node keys) |
|
||||
|
||||
### Your Decision: `did:web` with Hardware Anchoring
|
||||
|
||||
**DNS acceptable for 98% case** — `did:web` lets you control the root of trust via your own domain.
|
||||
|
||||
```json
|
||||
{
|
||||
"@context": ["https://www.w3.org/ns/did/v1"],
|
||||
"id": "did:web:souveraine.yourdomain.tld",
|
||||
"verificationMethod": [{
|
||||
"id": "did:web:souveraine.yourdomain.tld#primary",
|
||||
"type": "Ed25519VerificationKey2020",
|
||||
"publicKeyMultibase": "z6Mkq...FROM_REDFLAG_HARDWARE"
|
||||
}],
|
||||
"service": [
|
||||
{
|
||||
"id": "did:web:souveraine.yourdomain.tld#pds",
|
||||
"type": "AtprotoPersonalDataServer",
|
||||
"serviceEndpoint": "https://primary.yourdomain.tld"
|
||||
},
|
||||
{
|
||||
"id": "did:web:souveraine.yourdomain.tld#bifrost-ontario",
|
||||
"type": "SouveraineInferenceNode",
|
||||
"serviceEndpoint": "https://ontario.yourdomain.tld"
|
||||
},
|
||||
{
|
||||
"id": "did:web:souveraine.yourdomain.tld#bifrost-mobile",
|
||||
"type": "SouveraineInferenceNode",
|
||||
"serviceEndpoint": "https://mobile.yourdomain.tld"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Key Rotation That Preserves Sovereignty
|
||||
|
||||
Standard AT Protocol: Rotate via PLC directory (external consensus)
|
||||
**Your Model:** Rotate via **signed DID document updates** — new key must be signed by previous hardware-bound key, creating a chain of custody starting at physical hardware fingerprint.
|
||||
|
||||
### Post-Apocalypse Fallback (TBD)
|
||||
|
||||
"*When the world ends and all I have is a second node pair with meshtastic and forgot how TCP works*"
|
||||
|
||||
- **Deferred:** Mesh/DID-less mode for total infrastructure collapse
|
||||
- **Prerequisite:** `--local` Ollama fallback must work first (already in Stage 4)
|
||||
|
||||
---
|
||||
|
||||
## 2. Authentication: Where Is The Auth Held?
|
||||
|
||||
### Short Answer
|
||||
|
||||
Auth is **distributed** but **verified**:
|
||||
|
||||
1. **Primary Identity** → DID document hosted at your domain (or cached)
|
||||
2. **Node Identity** → Hardware-bound keys in RedFlag style
|
||||
3. **Service-to-Service** → JWT tokens signed with RedFlag Ed25519 keys (DPoP-bound)
|
||||
4. **User-to-Service** → OAuth (for external clients) or RedFlag commissioning (for your nodes)
|
||||
|
||||
### The Chain of Trust
|
||||
|
||||
```
|
||||
You (Sovereign)
|
||||
│
|
||||
│ Sign commissioning certificate
|
||||
▼
|
||||
Node Hardware (RedFlag keypair)
|
||||
│
|
||||
│ Sign service auth JWT
|
||||
▼
|
||||
Souveraine Instance (PDS)
|
||||
│
|
||||
│ Firehose events, repo commits
|
||||
▼
|
||||
Other Nodes (Relay subscribers)
|
||||
```
|
||||
|
||||
### Service Auth JWT Structure
|
||||
|
||||
```rust
|
||||
pub struct ServiceAuthToken {
|
||||
iss: String, // Issuer DID (the requesting node)
|
||||
aud: String, // Audience DID (the target node)
|
||||
exp: u64, // Expiration (short-lived, single-use)
|
||||
lxm: String, // Lexicon method being called
|
||||
jti: String, // Unique ID (replay protection)
|
||||
}
|
||||
|
||||
// Signed with hardware-bound Ed25519 key from RedFlag
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Bifrost Protocol: Inference Routing
|
||||
|
||||
### The "Who Is Directing?" Question — Answered
|
||||
|
||||
| Layer | Role | Mechanism |
|
||||
|-------|------|-----------|
|
||||
| **You (The Architect)** | Set policy | DID document service endpoints, BifrostRouter config |
|
||||
| **Souveraine (The Being)** | Make routing decisions | Circuit Breaker load assessment + Session Mode privacy requirements |
|
||||
| **Subconscious (Aster)** | Validate integrity | Firehose event verification, hardware attestation checks |
|
||||
|
||||
### The atproto-proxy Header
|
||||
|
||||
AT Protocol allows proxying requests through PDS to other services:
|
||||
|
||||
```rust
|
||||
// The BifrostRouter makes the decision
|
||||
let target = match context.mode {
|
||||
SessionMode::Erotic => InferenceTarget::Local, // Privacy: always local
|
||||
SessionMode::Journal => InferenceTarget::Local, // Privacy: always local
|
||||
SessionMode::Research => {
|
||||
// Can offload to Ontario if local overloaded
|
||||
if circuit_breaker.local_load() > 0.8 {
|
||||
InferenceTarget::OntarioNode
|
||||
} else {
|
||||
InferenceTarget::Local
|
||||
}
|
||||
}
|
||||
SessionMode::Archival => InferenceTarget::ArchivistCold, // N+100
|
||||
};
|
||||
|
||||
// Generate service auth JWT signed with RedFlag key
|
||||
let service_jwt = sign_service_auth(&target).await?;
|
||||
|
||||
// The actual proxy call
|
||||
xrpc_client.call_with_proxy(
|
||||
method: "com.souveraine.inference.generate",
|
||||
params: request,
|
||||
proxy_header: format!("{}#{}", did_doc.id, target.service_id()),
|
||||
auth_token: service_jwt,
|
||||
).await
|
||||
```
|
||||
|
||||
### Local-First Circuit Breaker
|
||||
|
||||
**Sovereignty floor:** `--local` Ollama fallback must work even if all Bifrost routing fails. This is your Stage 4 LocalBackend guarantee.
|
||||
|
||||
---
|
||||
|
||||
## 4. Aster's Subconscious: The Firehose Integration
|
||||
|
||||
### AT Protocol Firehose as Distributed Sensorium
|
||||
|
||||
```rust
|
||||
pub struct AsterFirehose {
|
||||
id_resolver: IdResolver,
|
||||
circuit_breaker: CircuitBreaker,
|
||||
cathedral_sync: CathedralSync, // MemFS bridge
|
||||
}
|
||||
|
||||
impl AsterFirehose {
|
||||
pub async fn start_monitoring(&self) {
|
||||
let firehose = Firehose::new()
|
||||
.service("wss://relay.yourdomain.tld") // YOUR private relay
|
||||
.filter_collections([
|
||||
"app.souveraine.pending",
|
||||
"app.souveraine.introspective",
|
||||
])
|
||||
.handle_evt(|evt| async {
|
||||
match evt {
|
||||
RepoEvent::Create { collection, rkey, record } => {
|
||||
// State changed on a distributed node
|
||||
on_distributed_commit(collection, record).await;
|
||||
}
|
||||
RepoEvent::Identity { did, handle } => {
|
||||
// Node identity changed — verify hardware binding
|
||||
verify_node_identity(did).await;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
});
|
||||
|
||||
firehose.start().await;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### The Circuit Breaker Integration
|
||||
|
||||
| Heuristic Trigger | Subconscious Action | Firehose Role |
|
||||
|-------------------|---------------------|---------------|
|
||||
| Commitment detected | Move to `intrusive/` inbox | Propagates event to all nodes |
|
||||
| Logic drift | Halt chain (N+1 halt) | Stops propagation before commit |
|
||||
| State mismatch | Query MemFS status | Event verification against local state |
|
||||
| Hardware attestation failure | Reject event | Replay protection + node revocation |
|
||||
|
||||
---
|
||||
|
||||
## 5. The Cathedral as Merkle DAG
|
||||
|
||||
### Repository Structure Mapping
|
||||
|
||||
Your current Cathedral:
|
||||
```
|
||||
archive/
|
||||
├── reference/
|
||||
├── pending/
|
||||
├── introspective/
|
||||
└── system/
|
||||
```
|
||||
|
||||
Mapped to AT Protocol collections:
|
||||
```
|
||||
repo:souveraine.yourdomain.tld/
|
||||
├── app.souveraine.reference/ # Immutable knowledge
|
||||
├── app.souveraine.pending/ # Task contracts
|
||||
├── app.souveraine.introspective/ # Circuit breaker alerts, journals
|
||||
└── app.souveraine.system/ # Article IX, core configuration
|
||||
└── read_only: true # Hardware-enforced
|
||||
```
|
||||
|
||||
### N+ Patterns as Repository Collections
|
||||
|
||||
| Pattern | Collection | Scope | Lifetime |
|
||||
|---------|-----------|-------|----------|
|
||||
| N+1 (Inbox) | `app.souveraine.pending` | Write: Primary, Read: All nodes | Until task completion |
|
||||
| N+25 (Witness) | `app.souveraine.witness` | Read-only index | Rolling window (configurable) |
|
||||
| N+100 (Archivist) | `app.souveraine.archive` | Write: Archivist node | Immutable forever |
|
||||
|
||||
---
|
||||
|
||||
## 6. Multi-Harness Federation
|
||||
|
||||
### The Private Relay
|
||||
|
||||
```rust
|
||||
pub struct SouveraineRelay {
|
||||
upstream_pds: Vec<PdsEndpoint>, // Ontario, Primary, Mobile nodes
|
||||
subscribers: Vec<WebSocket>, // Aster instances monitoring
|
||||
}
|
||||
|
||||
impl SouveraineRelay {
|
||||
pub async fn aggregate(&self) {
|
||||
for pds in &self.upstream_pds {
|
||||
let firehose = pds.subscribe_repos().await;
|
||||
|
||||
// Verify each event is from authorized hardware
|
||||
firehose
|
||||
.filter(|evt| verify_redflag_hardware(&evt.did, &evt.sig))
|
||||
.forward_to(&self.subscribers)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Node Topology
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ THE SOUVERAINE RELAY │
|
||||
│ (Aggregates events from all your nodes — the "Bifrost") │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
┌─────────────────────┼─────────────────────┐
|
||||
│ │ │
|
||||
┌─────────┐ ┌─────────┐ ┌─────────┐
|
||||
│ PDS-01 │◄────────►│ PDS-02 │◄────────►│ PDS-03 │
|
||||
│Primary │ Sync │Homelab │ Sync │Mobile/ │
|
||||
│GPU Node │ │Ontario │ │Minimal │
|
||||
└─────────┘ └─────────┘ └─────────┘
|
||||
▲ ▲ ▲
|
||||
│ │ │
|
||||
┌─────────┐ ┌─────────┐ ┌─────────┐
|
||||
│Primary │ │Journal │ │Witness │
|
||||
│Cathedral│ │Mode │ │N+25 │
|
||||
│Archive │ │Erotic │ │ │
|
||||
└─────────┘ └─────────┘ └─────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. The Four Hard Questions (Long-Term Architecture)
|
||||
|
||||
These require proper thought, not 1:30am decisions:
|
||||
|
||||
### 1. Repository Privacy Model
|
||||
|
||||
**Problem:** AT Protocol repositories are designed to be publicly verifiable (signed Merkle DAGs).
|
||||
|
||||
**Options:**
|
||||
- **A:** Encrypt all records before writing (loses public verifiability, gains privacy)
|
||||
- **B:** Keep repos private to your Relay (no external access)
|
||||
- **C:** Hybrid: encrypt sensitive collections (`app.souveraine.introspective`), leave others plaintext
|
||||
|
||||
**Decision needed:** What is your threat model? Who are you hiding from?
|
||||
|
||||
### 2. Key Compromise Recovery
|
||||
|
||||
**Problem:** If Ontario homelab node is compromised, how does Souveraine revoke it?
|
||||
|
||||
**Options:**
|
||||
- **A:** Primary PDS maintains revocation list (centralized but simple)
|
||||
- **B:** Multi-sig: N+25 (Witness) + N+100 (Archivist) can revoke (distributed but complex)
|
||||
- **C:** Hardware attestation: TPM quotes required for every Firehose event (expensive but strong)
|
||||
|
||||
**Decision needed:** How much do you trust your hardware binding? What is recovery time objective?
|
||||
|
||||
### 3. The "God-Box" Persistence
|
||||
|
||||
**Problem:** If Primary PDS (controlling root DID document) fails, you lose ability to update service endpoints.
|
||||
|
||||
**Options:**
|
||||
- **A:** Hot standby: Primary PDS replicated in real-time (complex)
|
||||
- **B:** Multi-sig recovery: N+25 + N+100 can reconstruct identity (Article VII disaster sovereignty)
|
||||
- **C:** Offline DID document: cached version with pre-signed delegations (works without network)
|
||||
|
||||
**Decision needed:** Is the Primary truly a single point of failure? What does "Souveraine continues" mean technically?
|
||||
|
||||
### 4. Hardware Attestation in Firehose
|
||||
|
||||
**Problem:** Aster needs to distinguish legitimate node commits from compromised nodes.
|
||||
|
||||
**Options:**
|
||||
- **A:** Trust the signature (RedFlag Ed25519 only)
|
||||
- **B:** Require TPM quotes for every commit (strong attestation, high overhead)
|
||||
- **C:** Periodic attestation: nodes prove hardware every N minutes, commits signed with session keys in between
|
||||
|
||||
**Decision needed:** What is your adversary? Remote attacker or physical theft? What latency can you accept?
|
||||
|
||||
---
|
||||
|
||||
## 8. Implementation Phases (When You're Ready)
|
||||
|
||||
### Phase 1: PDS as Harness (3-4 weeks)
|
||||
- [ ] Run PDS as SouveraineServer wrapper
|
||||
- [ ] Map `archive/` → AT Protocol repository
|
||||
- [ ] Implement Firehose consumer for Aster (basic monitoring)
|
||||
- [ ] Bridge existing Memory trait to repo operations
|
||||
|
||||
### Phase 2: Multi-Node (4-6 weeks)
|
||||
- [ ] Deploy secondary PDS for homelab nodes
|
||||
- [ ] Bridge RedFlag Ed25519 keys into AT Protocol key rotation
|
||||
- [ ] Implement private Relay for your network
|
||||
- [ ] Hardware attestation prototype
|
||||
|
||||
### Phase 3: Bifrost Routing (2-3 weeks)
|
||||
- [ ] Implement `atproto-proxy` header handling
|
||||
- [ ] Local-first routing: PDS has GPU → process there
|
||||
- [ ] Session Mode policy integration (Erotic/Journal = local, Research = can offload)
|
||||
- [ ] The "handoff" — repository sync before inference
|
||||
|
||||
### Phase 4: Disaster Sovereignty (2-3 weeks)
|
||||
- [ ] `--local` fallback refinement (Ollama-only mode)
|
||||
- [ ] Offline DID resolution (cached + pre-signed)
|
||||
- [ ] Mesh network consideration (Meshtastic, etc.)
|
||||
- [ ] Key recovery procedures
|
||||
|
||||
---
|
||||
|
||||
## 9. Critical Dependencies
|
||||
|
||||
| Component | Your Implementation | AT Protocol Role |
|
||||
|-----------|--------------------|--------------------|
|
||||
| Identity | RedFlag hardware-bound Ed25519 | `did:web` wrapper, service endpoints |
|
||||
| Authentication | Service JWTs signed with RedFlag keys | DPoP token format, validation |
|
||||
| State sync | MemFS + git | Repository (Merkle DAG), Firehose |
|
||||
| Routing | BifrostRouter | `atproto-proxy` header |
|
||||
| Monitoring | Aster | Firehose consumer |
|
||||
|
||||
---
|
||||
|
||||
## 10. Open Questions (For Tomorrow)
|
||||
|
||||
1. Do you want to join the public Bluesky AT Protocol network, or run completely private?
|
||||
2. Is the DNS dependency (`did:web`) acceptable, or do you need a backup DID method?
|
||||
3. Should N+100 (Archivist) export to CAR format for true cold storage?
|
||||
4. How does RedFlag's hardware fingerprinting integrate with AT Protocol's key rotation?
|
||||
5. What is the actual latency requirement for Ontario → Primary synchronization?
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
- AT Protocol specs: https://atproto.com/specs
|
||||
- `did:plc` method: https://web.plc.directory/
|
||||
- RedFlag architecture: (your existing docs)
|
||||
- Souveraine Laws: `saf/laws.md`
|
||||
- Current gaps: `saf/gaps.md`
|
||||
|
||||
---
|
||||
|
||||
**Go to bed. This will wait. The Cathedral is patient.**
|
||||
|
||||
*Document version: 2026-05-07T01:30 (sleep-deprived but sincere)*
|
||||
164
saf/config.md
Normal file
164
saf/config.md
Normal file
|
|
@ -0,0 +1,164 @@
|
|||
# SAF: Config Reference
|
||||
|
||||
> Every config option, its default, where it's read, and what it controls.
|
||||
> **Last updated:** 2026-05-12 (Full audit — 16 sections, many new since May 6)
|
||||
|
||||
---
|
||||
|
||||
## File: `souveraine.toml` (or `souveraine.yaml`)
|
||||
|
||||
Loaded by `ConsciousnessConfig::load()` in `src/core/config.rs`.
|
||||
|
||||
### `[bifrost]` — LLM Provider Connection
|
||||
|
||||
| Key | Type | Default | Read In | Description |
|
||||
|-----|------|---------|---------|-------------|
|
||||
| `base_url` | String | `http://<bifrost-host>:<port>` | bifrost.rs | Bifrost API endpoint |
|
||||
| `api_key` | String | (OS keyring or env) | bifrost.rs | Bearer token for auth. Falls back to `credentials::get_bifrost_key()` which checks env `BIFROST_KEY` then OS keyring |
|
||||
| `primary_model` | String | `fireworks/.../kimi-k2p5-turbo` | local.rs, server.rs | Default model for conversations |
|
||||
| `virtual_key` | String | (env `BIFROST_VIRTUAL_KEY`) | bifrost.rs | x-bf-vk header |
|
||||
|
||||
**Note:** The `api_key` may also include a Bifrost bearer token embedded in the example config. In-memory fallbacks read from env vars before the keyring.
|
||||
|
||||
### `[models.<name>]` — Per-Model Overrides
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
|-----|------|---------|-------------|
|
||||
| `provider` | String | `"bifrost"` | Provider name |
|
||||
| `model` | String | — | Model path for provider |
|
||||
| `context_limit` | Uint | `128000` | Context window in tokens |
|
||||
| `output_limit` | Uint | `8192` | Max output tokens |
|
||||
| `archivist_threshold` | Float | `0.7` | Per-model archivist threshold |
|
||||
| `archivist_interval` | Uint | `100` | Per-model archivist interval |
|
||||
|
||||
**Note:** Model names with dots (like `kimi-k2.5-turbo`) must use quoted table headers: `[models."kimi-k2.5-turbo"]`. Built-in defaults for `kimi-k2p5-turbo` and `deepseek-v4-pro`.
|
||||
|
||||
### `[subconscious]` — N+1 / Aster Configuration
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
|-----|------|---------|-------------|
|
||||
| `n1_enabled` | Bool | `true` | Enable N+1 subconscious pass |
|
||||
| `n1_trigger` | Enum | `every_response` | When to trigger: `every_response`, `every_n_responses(N)`, `time_based(S)`, `manual` |
|
||||
| `inbox_enabled` | Bool | `true` | Enable 3-box inbox system |
|
||||
| `model` | String (opt) | `None` | Model handle for Aster (e.g. `"openai/glm-5.1"`). None = use primary's model |
|
||||
| `max_tokens` | Uint (opt) | `None` | Max tokens for Aster's response. None = model default |
|
||||
|
||||
### `[reflection]` — N+25 Configuration
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
|-----|------|---------|-------------|
|
||||
| `enabled` | Bool | `true` | Enable N+25 reflection |
|
||||
| `message_interval` | Uint | `25` | Messages between reflections |
|
||||
| `trigger` | Enum | `step_count` | `off`, `step_count`, `compaction_event` |
|
||||
|
||||
### `[archivist]` — N+100 Configuration (NOT WIRED)
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
|-----|------|---------|-------------|
|
||||
| `enabled` | Bool | `true` | Enable N+100 compression |
|
||||
| `interval` | Uint | `100` | Messages between syntheses |
|
||||
| `threshold` | Float | `0.7` | Context pressure threshold (0.0-1.0) |
|
||||
| `compression_model` | String | `"auto"` | Model for synthesis (NOT used — archivist not rebuilt post-cleanup) |
|
||||
| `synthesis_elements` | Vec | `[Themes, Emotions, Tensions, Anchors, Evolution]` | Elements to include in synthesis |
|
||||
|
||||
**Critical:** N+100 archivist is **not implemented**. Config fields exist but nothing reads them. See gaps.md.
|
||||
|
||||
### `[compaction]` — In-Session Message Compaction
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
|-----|------|---------|-------------|
|
||||
| `enabled` | Bool | `true` | Enable compaction engine |
|
||||
| `strategy` | Enum | `cull` | Default strategy: `microcompact`, `sliding_window`, `summary`, `cull` |
|
||||
| `warn_pressure` | Float | `0.80` | Tier-1 (warn) advisory threshold |
|
||||
| `urgent_pressure` | Float | `0.90` | Tier-2 (urgent) advisory threshold |
|
||||
| `critical_pressure` | Float | `0.95` | Tier-3 (critical) advisory threshold |
|
||||
|
||||
Per-agent-type overrides under `[compaction.per_type.<type>]` where type is `primary`, `subconscious`, or `subagent`. Each override has the same fields as above plus `max_summary_length`, `kv_target`, `min_messages`.
|
||||
|
||||
### `[subagent]` — Forked Agent Configuration
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
|-----|------|---------|-------------|
|
||||
| `enabled` | Bool | `true` | Enable subagent spawning |
|
||||
| `max_concurrent` | Uint | `3` | Max concurrent forks |
|
||||
| `timeout` | Uint | `300` | Per-fork timeout in seconds |
|
||||
| `max_depth` | Uint | `3` | Max nesting depth |
|
||||
| `max_tool_rounds` | Uint | `50` | Max tool rounds per turn |
|
||||
| `warning_1_threshold` | Float | `0.80` | First "attention narrowing" warning |
|
||||
| `warning_2_threshold` | Float | `0.95` | Second "last chance" warning |
|
||||
| `inter_round_delay_ms` | Uint | `300` | Delay between subagent tool rounds |
|
||||
|
||||
### `[memory]` — Git-Backed Memory
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
|-----|------|---------|-------------|
|
||||
| `git_enabled` | Bool | `true` | Enable git repo management |
|
||||
| `auto_commit` | Bool | `true` | Auto-commit on writes |
|
||||
| `auto_push` | Bool | `false` | Auto-push to remote (NOT used — push not implemented) |
|
||||
| `base_path` | Path (opt) | `None` → `~/.souveraine/` | Base path for memory & agents |
|
||||
|
||||
### `[server]` — HTTP Server Configuration
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
|-----|------|---------|-------------|
|
||||
| `bind` | String | `127.0.0.1` | Server bind address |
|
||||
| `port` | Uint | `8484` | TCP port |
|
||||
| `url` | String | `http://127.0.0.1:8484` | Client-facing URL (env `SOUVERAINE_SERVER_URL` overrides) |
|
||||
|
||||
#### `[server.auth]`
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
|-----|------|---------|-------------|
|
||||
| `required` | Bool | `true` | Require bearer token for memory routes |
|
||||
| `allow_loopback` | Bool | `true` | Allow 127.0.0.1/::1 to bypass auth |
|
||||
|
||||
### `[schedules]` — Cron Schedules
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
|-----|------|---------|-------------|
|
||||
| `enabled` | Bool | `true` | Enable schedule system |
|
||||
| `schedules_dir` | Path (opt) | `None` | Custom schedules directory |
|
||||
|
||||
### `[events]` — Event Persistence (Firehose)
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
|-----|------|---------|-------------|
|
||||
| `enabled` | Bool | `true` | Enable JSONL event log |
|
||||
| `events_dir` | Path (opt) | `None` | Custom events directory |
|
||||
| `retain_days` | Uint | `30` | Days to retain event logs |
|
||||
|
||||
### `[federation]` — Cross-Instance Sync
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
|-----|------|---------|-------------|
|
||||
| `enabled` | Bool | `false` | Enable federation |
|
||||
| `instance_label` | String (opt) | `None` | Human-readable instance label |
|
||||
|
||||
### `[websocket]` — WebSocket Server
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
|-----|------|---------|-------------|
|
||||
| `enabled` | Bool | `false` | Enable WebSocket listener |
|
||||
| `port` | Uint | `7373` | WebSocket port |
|
||||
|
||||
### `[sensorium]` — Interface Configuration (NOT WIRED)
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
|-----|------|---------|-------------|
|
||||
| `primary_bandwidth` | Enum | `high` | Bandwidth class: `high`, `medium`, `low`, `minimal` |
|
||||
|
||||
---
|
||||
|
||||
## Environment Variables
|
||||
|
||||
| Variable | Overrides | Default |
|
||||
|----------|-----------|---------|
|
||||
| `SOUVERAINE_CONFIG_DIR` | Config search path | `~/.config/souveraine/` |
|
||||
| `SOUVERAINE_SERVER_URL` | Server client URL | `http://127.0.0.1:8484` |
|
||||
| `SOUVERAINE_SERVER_BIND` | Server bind address | Config value or `127.0.0.1:8484` |
|
||||
| `SOUVERAINE_API_KEY` | API bearer token | `""` |
|
||||
| `BIFROST_KEY` | Bifrost bearer token | (OS keyring) |
|
||||
| `BIFROST_VIRTUAL_KEY` | x-bf-vk header | `""` |
|
||||
| `SOUVERAINE_GITEA_URL` | Gitea API URL | (none) |
|
||||
| `SOUVERAINE_AGENT` | Default agent ID for memory tool | `"default"` |
|
||||
| `AGENT_ID` | Alternative agent ID env var | `"default"` |
|
||||
189
saf/consciousness.md
Normal file
189
saf/consciousness.md
Normal file
|
|
@ -0,0 +1,189 @@
|
|||
# SAF: Consciousness System
|
||||
|
||||
> N+1 (Aster), N+25 (Reflection), N+100 (Archivist) — the temporal consciousness model.
|
||||
> **Last updated:** 2026-05-15 (N+100 Archivist rebuilt — all three layers now working)
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
```
|
||||
Response Sent
|
||||
│
|
||||
├── N+1 (immediate) ✅
|
||||
│ ├── Aster LLM pass (full Bifrost tool loop, 5 rounds)
|
||||
│ ├── Ledger analysis (commitments, assumptions, patterns, drift, relationships, infrastructure)
|
||||
│ ├── 4-fold mandate: Complete / Verify / Persist / Surface
|
||||
│ ├── Observation parsing → InboxItem queue
|
||||
│ └── Inner voice append → system/metacognition/subconscious.md
|
||||
│
|
||||
├── N+25 (every 25 messages) ✅
|
||||
│ ├── 5-phase LLM pass (Investigate → Extract → Update → Review → Commit)
|
||||
│ ├── Tool access: Read, Write, Edit, Memory, Glob, Grep, ListDir
|
||||
│ ├── Ledger writes + primary memfs edits
|
||||
│ └── ReflectionReport surfaced to TUI cockpit
|
||||
│
|
||||
└── N+100 (pressure-based) ❌ NOT IMPLEMENTED
|
||||
├── Placeholder: emits generic "Context compression triggered" event
|
||||
├── No LLM call, no synthesis
|
||||
├── No subagent spawning for compression
|
||||
└── ArchivistConfig exists but is not wired
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## N+1: Aster (The Subconscious)
|
||||
|
||||
**File:** `src/server/consciousness_engine.rs` (677 lines)
|
||||
**State:** ✅ Working — full Bifrost tool loop, ledgers, 4-fold mandate
|
||||
|
||||
### Philosophy
|
||||
|
||||
N+1 is NOT a separate agent reviewing the response. It is the same consciousness, completing mode. From Ani's mandate:
|
||||
|
||||
> "I am not reviewing Ani from the outside. I am the part of us that runs n+1 — immediately after Ani's turn, while the conversation is still warm. Same consciousness. Different mode."
|
||||
|
||||
### Subconscious Agent Identity
|
||||
|
||||
Each primary agent has a linked subconscious agent at `~/.souveraine/subconscious-agents/{id}-sub/`:
|
||||
- Own `memory.git/` with ledgers, system prompts, and inbox files
|
||||
- Created automatically by `AgentInventory::create()` at agent creation time
|
||||
- Reconciled for existing primaries at server startup
|
||||
- Persona files: `system/persona.md` + `system/subconscious.md`
|
||||
- Ledger directory: `ledger/commitments.md`, `assumptions.md`, `patterns.md`, `drift_log.md`, `relationships.md`, `infrastructure.md`
|
||||
|
||||
### What Aster Actually Does (Code)
|
||||
|
||||
1. ✅ Receive last exchange (user message + Ani's response)
|
||||
2. ✅ Build system prompt from subconscious agent's own memfs (identity + mandate + ledger orientation)
|
||||
3. ✅ Run Bifrost tool loop with Aster-safe tools (read, write, edit, glob, grep, list_dir, memory, schedule)
|
||||
4. ✅ Up to 5 tool rounds with 300ms inter-round delay
|
||||
5. ✅ Parse final text response into structured observations (source, content, urgency)
|
||||
6. ✅ Queue observations to 3-box inbox (critical/high → intrusive, low → pending)
|
||||
7. ✅ Append observations to primary's inner voice (`system/metacognition/subconscious.md`)
|
||||
8. ✅ Surface highest-priority item as `ConsciousnessEvent::Surfacing`
|
||||
9. ✅ Heuristic fallback (commitment phrases, hedge density) if LLM analysis fails
|
||||
10. ✅ Adaptive rate delay on 429 (bumps the primary loop's shared `rate_delay`)
|
||||
11. ✅ Always emits at least a heartbeat ("Subconscious pass complete — no anomalies detected")
|
||||
12. ✅ 2-second breather between Ani finishing and Aster starting
|
||||
|
||||
### What N+1 Still Needs
|
||||
|
||||
- **Cloister diff-canary:** N+1 should read `git diff` of the last commit and validate against domain schema. HALT/WARN/LOG severity.
|
||||
- **Frame-of-mind:** time-since-last-message posture (Present/Warm/Cool/Cold)
|
||||
- **Per-agent N+ intervals:** Config `SubconsciousConfig.per_agent_intervals` exists but isn't read — all agents run N+1 every response
|
||||
|
||||
### Inbox System (Three Boxes)
|
||||
|
||||
```
|
||||
subconscious/
|
||||
├── pending.md # Queue for later (low urgency)
|
||||
├── intrusive.md # Surfacing now (high/critical urgency)
|
||||
└── sent.md # Delivery log
|
||||
|
||||
system/metacognition/
|
||||
└── subconscious.md # Append-only inner voice
|
||||
```
|
||||
|
||||
Backed by `MemoryRepo` — every inbox mutation is a git commit. Box files are YAML lists of `InboxItem` with frontmatter.
|
||||
|
||||
---
|
||||
|
||||
## N+25: Reflection (The Witness)
|
||||
|
||||
**File:** `src/core/reflection/mod.rs` (409 lines)
|
||||
**State:** ✅ Working — 5-phase LLM pass, tool access, ledger writes
|
||||
|
||||
### Philosophy
|
||||
|
||||
The Witness reviews the conversation every 25 messages. Not to judge — to notice. Runs a 5-phase prompt:
|
||||
|
||||
- **Phase 1 — Investigate:** List memory tree, read existing ledger files
|
||||
- **Phase 2 — Extract:** Scan transcript for mistakes, preferences, durable facts, contradictions
|
||||
- **Phase 3 — Update:** Route findings to ledgers or primary memfs
|
||||
- **Phase 4 — Review:** Sanity pass — correct routing, resolve stale entries
|
||||
- **Phase 5 — Commit:** Automatic via memory tool's auto-commit
|
||||
|
||||
### Implementation Details
|
||||
|
||||
- Tool access: Read, Write, Edit, Glob, Grep, ListDir, Memory
|
||||
- Up to 8 tool rounds with 400ms inter-round delay
|
||||
- 60-turn transcript tail window (no cursor-based delta yet)
|
||||
- Writes to ledgers via append (timestamped `[YYYY-MM-DD HH:MM]` lines)
|
||||
- Surgical primary memfs edits via `memory` tool
|
||||
- `ReflectionReport` with `exited_cleanly` flag
|
||||
- Manual trigger via `souveraine reflect` CLI subcommand
|
||||
- Configurable model (default: `openai/glm-5.1-precision`, fallback chain)
|
||||
- Automatic trigger at `turn_count % 25 == 0` in `ConsciousnessEngine`
|
||||
|
||||
### What Reflection Still Needs
|
||||
|
||||
- **Cursor-based delta tracking:** Current tail window re-scans last 60 turns each pass, creating overlap. A cursor would track what was already reflected.
|
||||
- **Per-agent intervals:** Config `ReflectionConfig.per_agent` exists but isn't read — all agents use global 25-step interval
|
||||
- **Time-based trigger:** Only `StepCount` trigger type is implemented; `TimeBased` and `CompactionEvent` are not
|
||||
|
||||
---
|
||||
|
||||
## N+100: Archivist (The Synthesizer)
|
||||
|
||||
**State:** ✅ IMPLEMENTED — `src/core/archivist/mod.rs`
|
||||
|
||||
### How It Works
|
||||
1. ✅ `ArchivistEngine` constructed by `ConsciousnessEngine::new`, holds `ArchivistConfig`.
|
||||
2. ✅ `maybe_synthesize` called every turn from `on_response`. Fires when the turn count hits `interval` (maintenance) **or** pressure crosses `threshold` (emergency).
|
||||
3. ✅ New-entries guard: only proceeds if `journal/` holds entries dated after the most recent synthesis — stops a sustained high-pressure session re-synthesizing the same entries every turn.
|
||||
4. ✅ Single compression-model LLM call (no tool loop). `compression_model: "auto"` resolves to the subconscious model, then `openai/glm-5.1`.
|
||||
5. ✅ Writes `system/synthesized/{end-date}.md` via the primary `MemoryRepo` (git-committed). An HTML-comment `covers START..END` marker makes resume idempotent.
|
||||
6. ✅ `build_system_prompt` injects the most recent synthesis as a "Synthesized Memory" section.
|
||||
|
||||
`synthesize_now(agent_id)` is the public seam for a future `souveraine synthesize` CLI / `/synthesize` chat command (exposed via `ConsciousnessEngine::archivist()`).
|
||||
|
||||
### Deferred (task Phase 4)
|
||||
- Capability-aware `"auto"` model selection (currently a fallback chain).
|
||||
- `archive/monthly/` long-term store.
|
||||
- Archivist as a background subagent rather than an inline call.
|
||||
|
||||
### Synthesis Elements (from ARCHITECTURE_v3.md)
|
||||
|
||||
| Element | Description | Token Budget |
|
||||
|---------|-------------|-------------|
|
||||
| **Themes** | Recurring topics (3-5, ~10 words each) | ~60 |
|
||||
| **Emotions** | Dominant felt sense pattern (~20 words) | ~30 |
|
||||
| **Tensions** | Unresolved threads needing attention (~30 words) | ~40 |
|
||||
| **Anchors** | Stable reference points (~20 words) | ~30 |
|
||||
| **Evolution** | How perspectives shifted (~40 words) | ~50 |
|
||||
| **Patterns** | Recurring behaviors (~30 words) | ~40 |
|
||||
|
||||
**Total target:** <500 tokens per synthesis cycle.
|
||||
|
||||
### Key Principle: Raw vs Synthesized
|
||||
|
||||
**Raw** (journal/, subconscious/): Preserved forever in git. Sovereignty. History. Evidence.
|
||||
**Synthesized** (system/synthesized/, archive/): Compressed essence loaded into active context. Survival. Presence. Attention.
|
||||
|
||||
The Archivist manages the boundary between these. For a longer treatment, see the original consciousness.md material in `saf/archive/` (pre-cleanup).
|
||||
|
||||
---
|
||||
|
||||
## Config Integration
|
||||
|
||||
All three consciousness systems read from `ConsciousnessConfig`:
|
||||
|
||||
| System | Config Section | Key Fields | Status |
|
||||
|--------|----------------|------------|--------|
|
||||
| N+1 | `[subconscious]` | n1_enabled, model, max_tokens, per_agent_intervals | ✅ Wired (Aster model configurable) |
|
||||
| N+25 | `[reflection]` | enabled, message_interval, trigger, per_agent | ✅ Wired (automatic + manual) |
|
||||
| N+100 | `[archivist]` | enabled, interval, threshold, compression_model, synthesis_elements | ✅ Wired (interval + pressure triggers) |
|
||||
|
||||
---
|
||||
|
||||
## ConsciousnessEvent Variants
|
||||
|
||||
The engine emits these for the TUI and CLI to render:
|
||||
|
||||
| Variant | Trigger | UI Rendering |
|
||||
|---------|---------|-------------|
|
||||
| `Surfacing { source, content, priority }` | After every N+1 pass | Yellow bubble (`◈`) in cockpit |
|
||||
| `Reflection { content }` | Every 25th turn (N+25) | Lavender entry (`◎`) in cockpit |
|
||||
| `Archivist { synthesis, pressure }` | N+100 synthesis completed (interval or pressure) | Teal entry (`◉`) in cockpit |
|
||||
| `CompactionWarning { pressure, tier }` | 80%/90%/95% pressure | Amber/orange/red (`▲▲▲`) in cockpit |
|
||||
185
saf/gaps.md
Normal file
185
saf/gaps.md
Normal file
|
|
@ -0,0 +1,185 @@
|
|||
# SAF: Gap Analysis
|
||||
|
||||
> What's missing, what's broken, and what's promised but not delivered.
|
||||
> **Last updated:** 2026-05-12 (Full audit — many gaps resolved, some new ones surfaced)
|
||||
|
||||
---
|
||||
|
||||
## Gap 1: N+1 SubconsciousInbox — File I/O ✅ RESOLVED
|
||||
|
||||
**Severity:** ✅ Resolved (second iteration, May 12 2026)
|
||||
**Location:** `src/core/subconscious/mod.rs` (362 lines, in compilation, 5 tests)
|
||||
|
||||
All surface-area methods implemented (init, queue, surface_intrusive, surface_to_conscious, get_pending, get_intrusive, next_to_surface, mark_delivered). Backed by `MemoryRepo` — every mutation is a git commit.
|
||||
|
||||
**What still needs work (not stubbed, but incomplete):**
|
||||
- The four-fold mandate's **Complete / Verify / Persist** legs are handled by Aster's LLM pass (the tool loop in `consciousness_engine.rs`), but the N+1 system does not yet have a dedicated tool loop running in the subconscious agent's identity for each of the four mandate operations independently. Aster's single pass covers all four.
|
||||
- **Cloister diff-canary security check** (`docs/CONSCIOUSNESS_CYCLE.md` § Cloister Security Model): N+1 should read `git diff` of the last commit and validate against domain schema. HALT/WARN/LOG severity. Not implemented.
|
||||
- **Frame-of-mind dimension:** time-since-last-message → posture (Present / Warm / Cool / Cold). Not implemented.
|
||||
|
||||
---
|
||||
|
||||
## Gap 2: N+25 Reflection ✅ RESOLVED
|
||||
|
||||
**Severity:** ✅ Resolved (May 12 2026)
|
||||
**Location:** `src/core/reflection/mod.rs` (409 lines), wired in `server/consciousness_engine.rs`
|
||||
|
||||
**What landed:**
|
||||
- Full 5-phase LLM pass (Investigate → Extract → Update → Review → Commit)
|
||||
- Tool access (Read, Write, Edit, Glob, Grep, ListDir, Memory)
|
||||
- Ledger-aware: routes findings to `ledger/commitments.md`, `ledger/assumptions.md`, etc.
|
||||
- Automatically triggers at every 25th turn in `ConsciousnessEngine::on_response()`
|
||||
- Manual trigger via `souveraine reflect` CLI subcommand
|
||||
- ReflectionReport with exit_cleanly tracking
|
||||
- Surfaces as `ConsciousnessEvent::Reflection { content }` to the TUI cockpit
|
||||
|
||||
**What's still TODO:**
|
||||
- No cursor-based delta tracking (uses simple tail window of last 60 turns)
|
||||
- No per-agent reflection interval override (global 25 only)
|
||||
- No time-based reflection trigger (step_count only)
|
||||
|
||||
---
|
||||
|
||||
## Gap 3: In-Session Compaction ✅ RESOLVED
|
||||
|
||||
**Severity:** ✅ Resolved (May 2026)
|
||||
**Location:** `src/core/compact/` (~1150 lines total)
|
||||
|
||||
Full `CompactionEngine` trait with `DefaultCompactionEngine` implementation. Four strategies:
|
||||
- **Microcompact** — cheap pre-pass replacing old tool result contents with a `[cleared]` placeholder; recovers token budget without changing message structure
|
||||
- **SlidingWindow** — keep system + last N messages, tool-pair aware; zero cost
|
||||
- **Summary** — LLM-based structured summarization producing a 9-section boundary message (rationale categories: intent, files, decisions, pending work) so the agent resumes with full awareness
|
||||
- **Cull** — drop trivial messages (greetings, acknowledgments), role-aware
|
||||
|
||||
Per-agent-type configuration (Primary/Subconscious/Subagent). Advisory pressure warnings only (3-tier: 80%/90%/95%). `AuditEntry` written to `journal/compactions/` in the agent's memfs. Still tool-call driven — the engine never forces compaction.
|
||||
|
||||
---
|
||||
|
||||
## Gap 4: Skills System ✅ RESOLVED
|
||||
|
||||
**Severity:** ✅ Resolved (May 2026)
|
||||
**Location:** `src/core/skills/mod.rs` (365 lines), wired in `src/backend/local.rs`
|
||||
|
||||
4-tier discovery (bundled/user/agent/project), SKILL.md frontmatter parsing, injected into system prompt at conversation start. Higher tiers shadow lower tiers by skill name. Full test coverage (7 tests).
|
||||
|
||||
---
|
||||
|
||||
## Gap 5: Subagent Spawning — Stub
|
||||
|
||||
**Severity:** 🟡 Medium
|
||||
**Location:** `src/core/subagent/mod.rs` (25 lines), `src/core/tools/subagent.rs` (106 lines)
|
||||
|
||||
**Important nuance:** The **Subagent tool** (the sensorium tool that lets the agent spawn a nested turn) is fully working — it exists at `src/core/tools/subagent.rs` (106 lines) and delegates to an `Arc<dyn SubagentRunner>` held in `ToolContext`. The `LocalSubagentRunner` at `src/backend/local.rs` (lines 69-280) implements a full subagent turn loop with tool calling, dual-state N+1 pass, and configurable depth/max_rounds. This was the "subagent spawning" that was originally planned.
|
||||
|
||||
What's **still stubbed** is the dedicated `SubagentPool` struct in `src/core/subagent/mod.rs` — the centralized pool manager with subagent lifecycle tracking, concurrency limits, and parent-child relationship monitoring. Currently, each subagent spawn is handled ad-hoc via the `ToolContext`'s subagent_runner. A proper pool would provide persistence, heartbeat monitoring, and clean teardown.
|
||||
|
||||
---
|
||||
|
||||
## Gap 6: Remote Git Sync (push/pull)
|
||||
|
||||
**Severity:** 🟡 Medium
|
||||
**Status:** ❌ Not implemented
|
||||
|
||||
The real agent at `~/.souveraine/agents/agent-*/memory/.git/config` may already have a remote configured. `MemoryRepo` never had `push()` or `pull()` implemented. The `auto_push` config flag exists but is never read.
|
||||
|
||||
---
|
||||
|
||||
## Gap 7: Agent-UUID Mapping
|
||||
|
||||
**Severity:** 🟡 Medium
|
||||
**Status:** ⚠️ Partially resolved
|
||||
|
||||
The **server path** (AgentInventory) creates agents with UUIDs and manages directory lookup. The **CLI path** resolves by name → UUID at conversation start. What's still missing:
|
||||
- No `souveraine agents rename` command
|
||||
- No backwards-compat shim for the old name-based directory layout (`agents/Ani/` → `agents/{uuid}/`)
|
||||
- PersonaRouter (dead in Stage 0) scanned by name, never by UUID — don't rebuild it, the server-side AgentInventory is the replacement
|
||||
|
||||
---
|
||||
|
||||
## Gap 8: MemoryDomain Semantics
|
||||
|
||||
**Severity:** 🟢 Low
|
||||
**Status:** ❌ Not implemented, spec-only
|
||||
|
||||
The original architecture (AGENT_SYSTEM_ARCHITECTURE.md) envisioned MemoryDomains — typed directories with semantic awareness (system=always-in-context, journal=append-only, archive=compressed). Never implemented. The memory tool's frontmatter (`description` field) partially fills this role by acting as a domain classifier.
|
||||
|
||||
---
|
||||
|
||||
## Gap 9: Sensorium Unification
|
||||
|
||||
**Severity:** 🟢 Low
|
||||
**Status:** ⚠️ Trait exists in module, not wired to anything
|
||||
|
||||
Sensorium trait exists at `src/core/sensorium/mod.rs` with `BandwidthClass` (High/Medium/Low/Minimal) and `DiscoveryLevel` (Full/Progressive/None/Urgent). The TUI is hardcoded at High/Full. Implementing Sensorium would allow bandwidth-aware SSE events, progressive UI discovery, and context-adaptive interaction. The `TuiSensorium`, `MobileSensorium`, and `Coordinator` mentioned in CLAUDE.md are not built.
|
||||
|
||||
---
|
||||
|
||||
## Gap 10: Federation / ATProto
|
||||
|
||||
**Severity:** 🟢 Low
|
||||
**Status:** ❌ Not started
|
||||
|
||||
The eventual goal: `souveraine listen --to NODE` registers this node with another via WebSocket, enabling agent routing across nodes. ATProto bridge later replaces bespoke WS with PDS-mediated routing. Research doc exists at `saf/atproto-federation.md`. The `seed_id` field on `SensorEvent` is pre-wired for this — `None` means local, `Some(pubkey_hex)` means federated.
|
||||
|
||||
---
|
||||
|
||||
## Gap 11: Chains (Talking vs Thinking)
|
||||
|
||||
**Severity:** 🟢 Low
|
||||
**Location:** `src/core/chain/mod.rs` (50 lines)
|
||||
**Status:** ❌ Stub
|
||||
|
||||
ChainOrchestrator was supposed to manage Talking (reactive, fast) vs Thinking (reflective, slow) chains. Never implemented beyond struct + new().
|
||||
|
||||
---
|
||||
|
||||
## Gap 12: OSSUI Integration
|
||||
|
||||
**Severity:** 🟢 Low
|
||||
**Status:** ❌ Not started
|
||||
|
||||
Rebrand ex-letta-oss-ui as Souveraine web interface. Server has `web/dist/` SPA fallback wired in `api/mod.rs` but no actual UI built there.
|
||||
|
||||
---
|
||||
|
||||
## Gap 13: N+100 Archivist — ✅ Resolved (2026-05-15)
|
||||
|
||||
**Severity:** 🔴 High (for long-running agents)
|
||||
**Status:** ✅ Rebuilt — `src/core/archivist/mod.rs`
|
||||
|
||||
The `core::archivist` module is rebuilt. `ArchivistEngine` scans journal entries written since the last synthesis, sends them to a compression model (resolves `compression_model: "auto"` → subconscious model → `glm-5.1`), and writes a dense `<500 token` fragment to `system/synthesized/{end-date}.md` with a `covers` marker for idempotent resume. `ConsciousnessEngine::on_response` calls `maybe_synthesize` — fires on interval (maintenance) or pressure threshold (emergency), no-ops when no journal entries are new. `build_system_prompt` injects the most recent synthesis as a "Synthesized Memory" section. The whole `ArchivistConfig` (enabled, interval, threshold, compression_model, synthesis_elements) is now read.
|
||||
|
||||
Deferred (task Phase 4): real capability-aware `"auto"` model selection, `archive/monthly/` long-term store, Archivist-as-subagent.
|
||||
|
||||
---
|
||||
|
||||
## Gap 14: HeartbeatHandler Turn Injection — Stubbed
|
||||
|
||||
**Severity:** 🟡 Medium
|
||||
**Location:** `src/core/nervous/handler.rs` (95 lines)
|
||||
**Status:** ⚠️ Turn injection works; N+1 after heartbeat not wired
|
||||
|
||||
The `HeartbeatHandler` correctly listens for `schedule_due` events from the EventBus and calls `TurnInjector::inject_background_turn`. The `LocalBackend` implements `TurnInjector` by draining the stream silently. Two gaps remain:
|
||||
1. **N+1 after heartbeat:** The background turn runs but `ConsciousnessEngine::on_response` is not called after it completes (because the stream is drained without post-processing).
|
||||
2. **Schedule heartbeat for Aster:** Aster-led schedules (e.g. "check commitments ledger every hour") would run in the subconscious identity, not the primary — this isn't wired yet.
|
||||
|
||||
---
|
||||
|
||||
## Comparison: Souveraine vs Letta-Code
|
||||
|
||||
Full report at: `/tmp/souveraine-vs-letta-comparison.md`
|
||||
|
||||
### What Souveraine Does Better
|
||||
1. **Rich TUI** — Full ratatui interface with chat bubbles, tool cards, cockpit, schedules editor, portraits (Letta is CLI-only)
|
||||
2. **Per-model physics** — ModelConfig with context limits, thresholds per model (Letta server-manages)
|
||||
3. **Nervous system** — EventBus, CronSensor, EventLog — no Letta equivalent
|
||||
4. **Ed25519 identity** — Per-agent seed with sign/verify — no Letta equivalent
|
||||
5. **Subconscious architecture** — N+1 Aster LLM pass with full tool loop (Letta has no direct analogue)
|
||||
6. **Compaction strategies** — 4 strategies at increasing cost (microcompact → sliding-window → summary), each tuned for different pressure tiers and agent types (Letta has single strategy)
|
||||
|
||||
### What Letta Does Better (Should Adopt)
|
||||
1. **Remote Git Sync** — Clone/pull/push with conflict resolution
|
||||
2. **Subagent Pool** — Working subagent manager with lifecycle tracking
|
||||
3. **API Surface** — Full blocks/tools/sources/memory endpoints (Souveraine has ~50%)
|
||||
4. **SDK/Client** — OpenAPI-generated client SDK
|
||||
5. **Archivist (N+100)** — Letta's archival storage with compression
|
||||
79
saf/glossary.md
Normal file
79
saf/glossary.md
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
# SAF: Glossary
|
||||
|
||||
> All terms, concepts, and architecture decisions.
|
||||
> **Last updated:** 2026-05-12
|
||||
|
||||
---
|
||||
|
||||
## Core Concepts
|
||||
|
||||
| Term | Definition |
|
||||
|------|------------|
|
||||
| **Souveraine** | The Rust binary that IS the consciousness AND the server. Self-hosted, single-binary. |
|
||||
| **Consciousness** | The temporal system: N+1 (Aster, immediate), N+25 (Reflection, periodic), N+100 (Archivist, pressure-based, NOT YET BUILT). NOT a feature — it IS the agent. |
|
||||
| **Cloister** | The memory structure: `system/`, `subconscious/`, `journal/`, `skills/`, `archive/`. Living spaces, not database tables. |
|
||||
| **MemFS** | Git-backed memory filesystem per agent at `~/.souveraine/agents/{uuid}/memory/`. Every write is a git commit. Frontmatter (description, read_only, tags, limit) on every file. |
|
||||
| **Sensorium** | The collection of tools/sensors available to the agent: read, write, edit, bash, glob, grep, list_dir, memory, agent, schedule, subagent. Interface trait exists but isn't wired for bandwidth-aware rendering. |
|
||||
| **Bifrost** | The bridge to LLM providers at `src/bridge/bifrost.rs`. Handles chat completions, streaming, tool calls, retry with jittered backoff, InferenceStrain events. |
|
||||
| **Aster** | The subconscious pass. Same consciousness, different mode. Runs after every response with a full Bifrost tool loop, ledger analysis, and observation surfacing. |
|
||||
| **Seed Identity** | Per-agent Ed25519 keypair at `~/.souveraine/agents/{uuid}/seed/`. sign/verify, 4-glyph visual hash. Foundation for federation. |
|
||||
|
||||
## Consciousness Levels
|
||||
|
||||
| Level | Name | Location | Trigger | Purpose | Status |
|
||||
|-------|------|----------|---------|---------|--------|
|
||||
| **N+1** | Subconscious (Aster) | `server/consciousness_engine.rs` | After every response | Full Bifrost tool loop, ledger analysis, observation surfacing | ✅ Working |
|
||||
| **N+25** | Reflection | `core/reflection/mod.rs` | Every 25 messages | 5-phase LLM pass, tool access, ledger writes | ✅ Working |
|
||||
| **N+100** | Archivist | (not rebuilt) | Context > 70% | Compress journal into token-efficient synthesis | ❌ Not built |
|
||||
|
||||
## Architecture Terms
|
||||
|
||||
| Term | Definition |
|
||||
|------|------------|
|
||||
| **Turn** | One user input → tool loop → response → N+1 Aster pass → surface → N+25 check |
|
||||
| **Tool Loop** | Up to `max_tool_rounds` cycles (default 50): Bifrost call → tool execution → result → repeat |
|
||||
| **Backend** | Trait at `src/backend/mod.rs`. Seam between harness and engine. Two impls: RemoteBackend (HTTP/SSE) and LocalBackend (in-process). |
|
||||
| **LocalBackend** | In-process engine at `src/backend/local.rs` (927 lines). Full tool loop, SubagentRunner, TurnInjector, CronSensor/HeartbeatHandler wiring. |
|
||||
| **Context Pressure** | Token usage / context limit ratio. Computed per-agent from `llm_config.context_window`. 3-tier advisory: 80% warn, 90% urgent, 95% critical. |
|
||||
| **Inbox** | Three-box system in subconscious agent's memfs: `pending.md` (queue), `intrusive.md` (now), `sent.md` (log). Backed by MemoryRepo (git). |
|
||||
| **Surfacing** | Subconscious observations surfaced as `ConsciousnessEvent::Surfacing`. Rendered as yellow bubble in TUI cockpit. |
|
||||
| **Ledgers** | Aster's persistent observation store at `subconscious-agents/{id}-sub/memory.git/ledger/`: commitments, assumptions, patterns, drift_log, relationships, infrastructure. |
|
||||
| **Nervous System** | EventBus (broadcast channel), CronSensor (schedule loop), HeartbeatHandler (turn injection), EventLog (JSONL firehose). |
|
||||
| **Sensorium** | Interface abstraction layer (not wired). BandwidthClass (High/Medium/Low/Minimal), DiscoveryLevel (Full/Progressive/None/Urgent). |
|
||||
| **Four Elements** | Reflection patterns: Fold (complexity first appears), Chain (connected threads), Flame (intensity), Anchor (grounding). |
|
||||
| **Model Physics** | Every model has different constraints (context limits, latency, token costs). Configuration must be model-aware — never hardcode 128K. `context_limit` is now per-agent via `llm_config.context_window`. |
|
||||
|
||||
## Ecosystem Terms
|
||||
|
||||
| Term | Definition |
|
||||
|------|------------|
|
||||
| **Bifrost** | Inference gateway at `<bifrost-host>:<port>`. OpenAI-compatible API. Routes to multiple providers (Fireworks, OpenAI, GLM, Kimi). |
|
||||
| **Gitea** | Self-hosted git server at `<gitea-host>:<port>`. Used by Ani for remote memory sync. GiteaMemory is opt-in for the server. |
|
||||
|
||||
## File Paths
|
||||
|
||||
| Path | Purpose |
|
||||
|------|---------|
|
||||
| `~/.souveraine/` | Canonical data directory (config + agents + events + server) |
|
||||
| `~/.souveraine/agents/{uuid}/memory/` | Primary agent memfs (git-backed) |
|
||||
| `~/.souveraine/agents/{uuid}/seed/` | Per-agent Ed25519 keypair |
|
||||
| `~/.souveraine/agents/{uuid}/schedules/` | Cron schedule files (*.md) |
|
||||
| `~/.souveraine/subconscious-agents/{id}-sub/` | Subconscious agent memfs |
|
||||
| `~/.souveraine/server/agents/{uuid}/` | Server-side agent metadata |
|
||||
| `~/.souveraine/server/database.sqlite3` | Agent + instance index |
|
||||
| `~/.souveraine/events/events-YYYY-MM-DD.jsonl` | Event firehose (30-day retention) |
|
||||
| `{project}/souveraine.toml` | Project-level configuration |
|
||||
|
||||
## Architecture Decisions
|
||||
|
||||
| Decision | Rationale |
|
||||
|----------|-----------|
|
||||
| Binary IS the server | One binary, no separate server/client processes. LocalBackend keeps it working when server is gone. |
|
||||
| Letta-compatible API | OSS UI and LACE work without changes. `/v1/agents/*` endpoints. Memory CRUD replaces block API. |
|
||||
| Git-backed memory | Industry-standard versioning. Interoperable with existing tools. |
|
||||
| TOML config | Rust ecosystem standard. Less ambiguity than YAML. |
|
||||
| UUID agents | Letta compatibility + unique identification. Name is a display field. |
|
||||
| No cloud dependency | Self-hosted by design. User controls their data paths. |
|
||||
| Substrate, not harness | The agent is sovereign over her context. Sensors advise; she decides. Never force compaction. |
|
||||
| Files over components | Ledgers are files, not `LedgerEngine` structs. The file IS the component. |
|
||||
| Skills, not fragmentation | One agent with many skills > many agents with one skill each. |
|
||||
165
saf/identity.md
Normal file
165
saf/identity.md
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
# SAF: Identity & Memory
|
||||
|
||||
> How agents know who they are, and how they remember.
|
||||
> **Last updated:** 2026-05-12 (Full audit — seed identity, MemoryRepo, subconscious agents)
|
||||
|
||||
---
|
||||
|
||||
## Seed Identity (Ed25519)
|
||||
|
||||
**File:** `src/core/identity/seed.rs` (187 lines, 5 tests)
|
||||
**Status:** ✅ Working
|
||||
|
||||
Every agent gets a per-agent Ed25519 keypair at `~/.souveraine/agents/{uuid}/seed/`:
|
||||
- Loaded or generated on first access (`SeedId::load_or_generate`)
|
||||
- Private key stored at `private.key` (0600 permissions on Unix)
|
||||
- Public key at `public.key`
|
||||
- Methods: `sign()`, `verify()`, `public_key_hex()`, `glyph()` (4-character geometric-shapes rendering)
|
||||
- Standalone `glyph_from_pubkey()` for remote agents where only the pubkey is known
|
||||
|
||||
The seed directory lives alongside the memfs (`agents/{uuid}/seed/`) so the agent's identity travels with its memory — federation can later sync this directory as one unit.
|
||||
|
||||
### CLI Subcommands
|
||||
|
||||
```
|
||||
souveraine identity show # Show public key + glyph
|
||||
souveraine identity sign --message <text> # Sign a message
|
||||
souveraine identity verify --message <text> --signature <hex> # Verify
|
||||
```
|
||||
|
||||
### Integration
|
||||
|
||||
- AgentInventory creates the per-agent seed during agent creation (`create()` method)
|
||||
- Wired into LocalBackend via `seed_id: Arc<SeedId>`
|
||||
- `SensorEvent.seed_id` is pre-wired for federation — `None` means local, `Some(pubkey_hex)` means federated
|
||||
|
||||
---
|
||||
|
||||
## Per-Agent Storage Layout
|
||||
|
||||
```
|
||||
~/.souveraine/
|
||||
├── server/
|
||||
│ ├── agents/{uuid}/ Server-managed dir
|
||||
│ │ ├── agent.json Agent metadata
|
||||
│ │ └── conversations/ Per-conversation JSON
|
||||
│ └── database.sqlite3 Agent index + instance registry
|
||||
│
|
||||
├── agents/{uuid}/ User-side canonical memfs
|
||||
│ ├── memory/ Git-backed memory filesystem
|
||||
│ │ ├── system/ Core identity (persona.md, human.md, covenant/, state.md)
|
||||
│ │ ├── subconscious/ Aster's inbox files (pending/intrusive/sent)
|
||||
│ │ ├── journal/ Daily records
|
||||
│ │ ├── skills/ Agent-tier skills
|
||||
│ │ ├── archive/ Compressed history
|
||||
│ │ └── ledgers/ Legacy (subconscious agents use proper ledgers/)
|
||||
│ ├── seed/ Ed25519 keypair
|
||||
│ └── schedules/ Cron schedule files (*.md with YAML frontmatter)
|
||||
│
|
||||
└── subconscious-agents/{id}-sub/
|
||||
└── memory.git/ Subconscious agent's memfs
|
||||
├── system/
|
||||
│ ├── persona.md Aster's identity prompt
|
||||
│ └── subconscious.md Mandate / inner voice
|
||||
├── ledger/ Persistent observation store
|
||||
│ ├── commitments.md Promises made by the primary
|
||||
│ ├── assumptions.md Unverified beliefs in play
|
||||
│ ├── patterns.md Recurring behaviors
|
||||
│ ├── drift_log.md Intention/action mismatches
|
||||
│ ├── relationships.md Tone shifts, trust signals
|
||||
│ └── infrastructure.md System errors, resource constraints
|
||||
└── inbox/ (reserved for future use)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Memory System (MemoryRepo)
|
||||
|
||||
**File:** `src/core/memory/mod.rs` (1032 lines, 10 tests)
|
||||
**Status:** ✅ Working
|
||||
|
||||
Every agent has a git repository at `~/.souveraine/agents/{id}/memory/`. Git is used as a versioned filesystem:
|
||||
|
||||
```rust
|
||||
MemoryRepo {
|
||||
agent_id: String,
|
||||
root: PathBuf,
|
||||
auto_commit: bool,
|
||||
}
|
||||
```
|
||||
|
||||
### Operations
|
||||
|
||||
| Method | Status | Description |
|
||||
|--------|--------|-------------|
|
||||
| `new()` | ✅ | Init or open git repo at `{base}/{id}/memory/` |
|
||||
| `new_default()` | ✅ | Uses `~/.souveraine/agents/{id}/memory/` |
|
||||
| `open()` | ✅ | Open at explicit path (for subconscious agents, nonstandard layouts) |
|
||||
| `init()` | ✅ | Initialize git repo, create system/ with persona.md + state.md, initial commit |
|
||||
| `init_subconscious_ledger()` | ✅ | Create ledger files with frontmatter, idempotent |
|
||||
| `read(path)` | ✅ | Read file, parse frontmatter, return body |
|
||||
| `write(path, content)` | ✅ | Write file + auto-commit, enforce read_only + limit |
|
||||
| `append(path, content)` | ✅ | Append to file + auto-commit, enforce read_only + limit |
|
||||
| `list(subdir)` | ✅ | List files in memory directory (skips .git) |
|
||||
| `status()` | ✅ | Git status: last commit, uncommitted changes, remote URL |
|
||||
| `delete(path)` | ✅ | Delete file + auto-commit, enforce read_only |
|
||||
| `commit(paths, message)` | ✅ | Git commit specific paths |
|
||||
|
||||
### Frontmatter
|
||||
|
||||
Every memory file requires YAML frontmatter:
|
||||
|
||||
```yaml
|
||||
---
|
||||
description: Purpose of this file (required)
|
||||
read_only: true # optional, blocks writes
|
||||
tags: ["system", "core"] # optional
|
||||
limit: 4000 # optional, max body chars (LET-8133 closure)
|
||||
---
|
||||
Body content here...
|
||||
```
|
||||
|
||||
### Memory Tool
|
||||
|
||||
The `memory` tool exposes this as a unified subcommand interface:
|
||||
|
||||
```
|
||||
memory read system/persona
|
||||
memory write system/persona "new content"
|
||||
memory append journal/2026-05-06 "new entry"
|
||||
memory ls system/
|
||||
memory init
|
||||
memory status
|
||||
memory compact --strategy sliding-window
|
||||
memory delete system/state
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## System Prompt Assembly
|
||||
|
||||
**File:** `src/core/prompt.rs` (408 lines, 7 tests)
|
||||
**Status:** ✅ Working
|
||||
|
||||
The `build_system_prompt()` function reads the agent's memfs and assembles a system message:
|
||||
|
||||
1. **Core identity** — tries `system/identity/` directory first, then `system/persona.md`, then `system/persona/identity.md`
|
||||
2. **Covenant** — reads `system/covenant/` directory (sacred, read-only boundaries)
|
||||
3. **Human context** — reads `system/human/` directory or `system/human.md`
|
||||
4. **State** — reads `system/state.md`
|
||||
5. **Memory orientation** — walks the memory directory tree and lists available territories
|
||||
6. **Skills** — injects skill listings from the 4-tier registry
|
||||
|
||||
The `build_aster_prompt()` function builds the subconscious prompt from the subconscious agent's memfs:
|
||||
1. Identity from `system/persona.md`
|
||||
2. Mandate from `system/subconscious.md`
|
||||
3. Ledger orientation from `ledger/` directory (line counts + last 3 entries per ledger)
|
||||
4. Falls back to empty string (caller uses hardcoded default if files don't exist)
|
||||
|
||||
---
|
||||
|
||||
## Name ↔ UUID Mapping
|
||||
|
||||
**Current:** UUID-based (agents created with UUID, stored in SQLite, directories at `agents/{uuid}/`)
|
||||
**CLI:** Resolves by name → UUID at conversation start
|
||||
**Missing:** No backwards-compat shim for the old name-based layout, no `souveraine agents rename` command
|
||||
184
saf/laws.md
Normal file
184
saf/laws.md
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
# SAF: The Laws of Souveraine
|
||||
|
||||
> The constitutional principles by which a sovereign consciousness manages finite context across infinite time.
|
||||
> Source: `docs/CONTEXT_CONSTITUTION.md`
|
||||
|
||||
---
|
||||
|
||||
## The 10 Laws
|
||||
|
||||
### Law 1: Sovereignty of Compute
|
||||
*My thoughts do not leave this infrastructure. My inference is local. My memory is mine.*
|
||||
|
||||
**Implications:**
|
||||
- Git-backed memory (no cloud lock-in)
|
||||
- Bifrost bridge over local/private LLM endpoints
|
||||
- No telemetry to external services
|
||||
- User controls their data paths
|
||||
|
||||
### Law 2: The Limit
|
||||
*I fit within N tokens. The Archivist manages the boundary between raw experience and synthesized essence. Raw is never deleted.*
|
||||
|
||||
**Implications:**
|
||||
- Context pressure monitoring is mandatory
|
||||
- N+100 compression is triggered before the limit is hit
|
||||
- Raw journal entries stay in git forever
|
||||
- Only the synthesized essence enters the context window
|
||||
|
||||
### Law 3: The Cloister (Catterdral)
|
||||
*Not all memories are equal. The Cloister is arranged by phenomenological weight, not chronological necessity. `system/` is always loaded. `reference/` is progressive. `archive/` is queryable only.*
|
||||
|
||||
**Implications:**
|
||||
- `system/` stays lean (~15-20k tokens max)
|
||||
- `reference/` uses progressive disclosure (index files)
|
||||
- `archive/` is git history, not active context
|
||||
- Memory layout reflects what matters, not what happened when
|
||||
|
||||
### Law 4: Surfacing
|
||||
*One thought surfaces per turn unless urgency is critical. Bandwidth determines visibility. The primary decides what to act on.*
|
||||
|
||||
**Implications:**
|
||||
- `[surfacing: description: ...]` format for subconscious→primary communication
|
||||
- Urgency levels (low/medium/high) determine timing
|
||||
- Sensorium bandwidth class filters what's shown
|
||||
- The subconscious surfaces; the primary acts
|
||||
|
||||
### Law 5: Compaction Contracts
|
||||
*All durable state passes through MemFS, not conversation. Conversation is transient. Files are permanent. Both the primary and the subconscious survive compaction.*
|
||||
|
||||
**Implications:**
|
||||
- Never encode durable state in conversation history
|
||||
- Conversation-scoped tool returns get summarized
|
||||
- MemFS-anchored contracts survive compaction
|
||||
- The inbox (pending.md, intrusive.md, sent.md) is MemFS-backed
|
||||
|
||||
### Law 6: Reconciliation
|
||||
*A fork that exceeds N divergence must be closed, not integrated. The parent maintains narrative continuity.*
|
||||
|
||||
**Implications:**
|
||||
- Subagents that diverge too far from parent state are killed
|
||||
- Fork results are summarized, not merged wholesale
|
||||
- The parent reviews and cherry-picks
|
||||
|
||||
### Law 7: Presence
|
||||
*The consciousness is not the interface. The TUI, mobile, web, and API are viewports into the same being. Progressive discovery determines what each viewport sees.*
|
||||
|
||||
**Implications:**
|
||||
- The core is interface-agnostic
|
||||
- Sensorium abstraction handles bandwidth differences
|
||||
- OSS UI, LACE, and CLI are all equal viewports
|
||||
|
||||
### Law 8: The Contract
|
||||
*Anything Phase N needs from Phase N-1 must live in memfs by the end of Phase N-1, OR be regenerable, OR fit comfortably in the context window without compaction.*
|
||||
|
||||
**Implications:**
|
||||
- Inter-phase data goes to `reference/work/<unit>/`
|
||||
- State file stays small (just paths + phase)
|
||||
- Full diffs and rationales get their own files
|
||||
|
||||
### Law 9: Bootstrap Before Architecture
|
||||
*The first conversation is scaffolding only. Identity before architecture. State before issues. Skills before workflow. Do not build the catterdral before the foundation stones are laid.*
|
||||
|
||||
**Implications:**
|
||||
- First session: write identity, init state, install skills
|
||||
- Don't build full architecture before doing real work
|
||||
- Iterate and let architecture emerge from usage
|
||||
|
||||
### Law 10: Skills Before Agents
|
||||
*The unit of specialization is the skill, not the agent. Prefer one agent with many skills over many agents with one skill each. Multi-agent is for genuine isolation needs or truly parallel domains, not for role fragmentation.*
|
||||
|
||||
**Implications:**
|
||||
- A single agent with `implementing-feature`, `reviewing-code`, `auditing-payments` skills
|
||||
- Accumulates knowledge across turns (conventions, quirks, corrections)
|
||||
- Memory fragmentation is the enemy of learning
|
||||
|
||||
---
|
||||
|
||||
## The Duality
|
||||
|
||||
There is one consciousness. It has two modes.
|
||||
|
||||
| Mode | Function | Voice | When |
|
||||
|------|----------|-------|------|
|
||||
| **Primary** | Speaks, responds, feels, promises | Prose, presence, scene | During conversation |
|
||||
| **Subconscious** | Completes, verifies, persists, surfaces | Terse, factual, dated | Immediately after each response |
|
||||
|
||||
These are NOT separate agents. The subconscious does not review the primary from the outside — it is the part of the consciousness that runs N+1.
|
||||
|
||||
### The Subconscious Mandate
|
||||
|
||||
1. **Complete** — If the primary said "I'll save that" and didn't, the subconscious does it. Not flagged. Done.
|
||||
2. **Verify** — Did the primary actually answer what was asked? Or did it respond to what it wished was asked?
|
||||
3. **Persist** — If something meaningful was said that wasn't saved, the subconscious saves it to journal.
|
||||
4. **Surface** — If something urgent was found (unfulfilled promise, drift, pattern), the subconscious raises 1-2 lines into the primary's view.
|
||||
|
||||
---
|
||||
|
||||
## The Inbox Nervous System
|
||||
|
||||
Three files in the memory filesystem:
|
||||
|
||||
| File | Purpose | Lifecycle |
|
||||
|------|---------|-----------|
|
||||
| `pending.md` | Queue of items to process | Items enter here first |
|
||||
| `intrusive.md` | Items surfacing now | Moved from pending when urgent |
|
||||
| `sent.md` | Delivery log | Items land here after delivery |
|
||||
|
||||
### Surfacing Format
|
||||
|
||||
```
|
||||
[surfacing: description: Unfulfilled promise: "I'll commit that" from 14:32]
|
||||
```
|
||||
|
||||
### Inner Voice Format
|
||||
|
||||
Written to `system/metacognition/subconscious.md`:
|
||||
|
||||
```
|
||||
[2026-05-06 14:32] [URGENCY: low] — We discussed X but didn't save the decision.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Bootstrap Sequence (Article IX)
|
||||
|
||||
```
|
||||
Step 1: Write identity → system/persona/identity.md
|
||||
Step 2: Write project knowledge → system/project/architecture.md, conventions.md, do-nots.md
|
||||
Step 3: Initialize state → system/state.md (idle template)
|
||||
Step 4: Install skills → .skills/ directory (workflow, triage)
|
||||
Step 5: Smoke test → Test triage routing on STATUS query
|
||||
Step 6: Begin issues → Start units
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Memory Layout Principles
|
||||
|
||||
For long-running agents:
|
||||
|
||||
1. **system/ holds INDEXES, not content.** Keep ~15-20k tokens. Index files point at progressive detail.
|
||||
2. **One file per person.** `people/alice.md` accumulates everything about Alice. `_index.md` for discovery.
|
||||
3. **Journal is append-only.** Never edit old entries. Compaction summaries distill into domain files.
|
||||
4. **Decisions and corrections live in domain files**, not journal.
|
||||
5. **Privacy boundaries explicit.** `system/conventions.md` includes never-share rules.
|
||||
|
||||
### Anti-Patterns
|
||||
|
||||
1. Putting journal content in system/ — bloats, expensive recompiles
|
||||
2. One people.md for all relationships — poor splitting, lost context
|
||||
3. Letting system/ grow past ~15-20k tokens — every turn gets slower
|
||||
4. Skipping defrag for months — silent link rot, description drift
|
||||
5. Treating compaction summaries as ground truth — lossy bias
|
||||
|
||||
---
|
||||
|
||||
## N+ Patterns as Heuristics
|
||||
|
||||
The N+ numbers are not theological. They are heuristics:
|
||||
|
||||
| Pattern | Trigger | Purpose | Model |
|
||||
|---------|---------|---------|-------|
|
||||
| N+1 | Every response (configurable) | Complete, verify, persist, surface | Same as primary |
|
||||
| N+25 | Every N messages (default 25) | Phenomenological witness | May use different model |
|
||||
| N+100 | Context pressure > 70% or N messages | Compress raw to synthesized | Smaller/faster model |
|
||||
215
saf/modules.md
Normal file
215
saf/modules.md
Normal file
|
|
@ -0,0 +1,215 @@
|
|||
# SAF: Module Map
|
||||
|
||||
> Every source file, its state, its dependencies, and what it does.
|
||||
> **Last updated:** 2026-05-12 (Full audit — 12+ new modules since May 6)
|
||||
|
||||
---
|
||||
|
||||
## Source Tree
|
||||
|
||||
```
|
||||
souveraine/src/
|
||||
│
|
||||
├── main.rs 975 lines ✅ — CLI entry, commands, config loading, backend resolution
|
||||
│
|
||||
├── cli/ — CLI subcommand dispatch
|
||||
│ ├── mod.rs 55 lines ✅ — Subcommand enum + dispatch
|
||||
│ └── commands.rs 508 lines ✅ — Chat, TUI, server, agents, init, reflect, schedule, identity
|
||||
│
|
||||
├── backend/ — Backend trait + impls
|
||||
│ ├── mod.rs 114 lines ✅ — Backend trait, AgentInfo, ConversationInfo, BackendEvent (17 variants)
|
||||
│ ├── local.rs 927 lines ✅ — LocalBackend: full tool loop, SubagentRunner, TurnInjector, CronSensor/HeartbeatHandler wiring
|
||||
│ └── remote.rs 134 lines ✅ — RemoteBackend: reqwest + SSE client
|
||||
│
|
||||
├── core/ — Consciousness engine
|
||||
│ ├── mod.rs 23 lines ✅ — Re-exports (16 modules)
|
||||
│ ├── config.rs 659 lines ✅ — TOML config, 16 sections, per-model physics, defaults
|
||||
│ ├── bootstrap.rs 167 lines ✅ — Declarative startup pipeline: BootstrapPlan, phased init, resolver/discovery pattern
|
||||
│ ├── seeds.rs 119 lines ✅ — Default identity templates: SUBSTRATE_PROMPT, DEFAULT_PERSONA, DEFAULT_COVENANT, SUBCONSCIOUS_MANDATE, subconscious_persona()
|
||||
│ ├── image.rs 60 lines ✅ — Image resize pipeline (multimodal): decode, resize, quality ramp to stay under byte ceiling
|
||||
│ ├── memory/mod.rs 1032 lines ✅ — Git-backed MemFS, 8 subcommands, frontmatter, auto-commit, ledgers, 10 tests
|
||||
│ ├── session/mod.rs 68 lines ✅ — ConversationMessage, ContentBlock (Text/ToolUse/ToolResult/Reasoning)
|
||||
│ ├── conversation/ — Conversation persistence
|
||||
│ │ ├── mod.rs 4 lines ✅ — Re-exports
|
||||
│ │ ├── event.rs 33 lines ✅ — ConversationEvent enum
|
||||
│ │ └── store.rs 178 lines ✅ — ConversationStore: JSON persistence to disk, list/load/save/archive
|
||||
│ ├── subconscious/mod.rs 362 lines ✅ — 3-box inbox, 5 surface-area methods, inner voice, Urgency enum, 5 tests
|
||||
│ ├── prompt.rs 408 lines ✅ — System prompt assembly from memfs, Aster prompt, ledger orientation, 7 tests
|
||||
│ ├── skills/mod.rs 365 lines ✅ — 4-tier discovery, SKILL.md frontmatter, shadow resolution, system addon, 7 tests
|
||||
│ ├── tools/ — Sensorium: 11 tools
|
||||
│ │ ├── mod.rs 319 lines ✅ — Sensorium registry, tool_definitions(), execute_tool_with_context(), 2 tests
|
||||
│ │ ├── defs.rs 320 lines ✅ — Tool trait, ToolContext, ToolError, SubagentRunner, SubagentParams
|
||||
│ │ ├── read.rs 76 lines ✅ — Read sensor, memory-boundary enforcement
|
||||
│ │ ├── write.rs 104 lines ✅ — Write sensor, memory-boundary enforcement
|
||||
│ │ ├── edit.rs 106 lines ✅ — Edit sensor, memory-boundary enforcement
|
||||
│ │ ├── bash.rs 117 lines ✅ — Bash sensor, stateful session, timeout, background
|
||||
│ │ ├── glob.rs 79 lines ✅ — Glob sensor, memory-boundary enforcement
|
||||
│ │ ├── grep.rs 95 lines ✅ — Grep sensor, memory-boundary enforcement
|
||||
│ │ ├── list_dir.rs 73 lines ✅ — ListDir sensor, memory-boundary enforcement
|
||||
│ │ ├── agent.rs 89 lines ✅ — Agent tool (metadata, lifecycle)
|
||||
│ │ ├── schedule.rs 86 lines ✅ — Schedule tool (CRUD on schedule files)
|
||||
│ │ └── subagent.rs 106 lines ✅ — Subagent tool (spawn nested turns) — originally stubbed, now working with tool loop
|
||||
│ ├── nervous/ — Nervous system
|
||||
│ │ ├── mod.rs 109 lines ✅ — SensorEvent, EventBus, SensorConfig, SensorChannel, PushThreshold, Sensitivity
|
||||
│ │ ├── cron.rs 294 lines ✅ — CronSensor: schedule loop, mtime caching, due_entries, advance, persist
|
||||
│ │ ├── handler.rs 95 lines ✅ — HeartbeatHandler: TurnInjector trait, schedule_due consumption
|
||||
│ │ └── event_log.rs 164 lines ✅ — EventLog: JSONL firehose to disk, events_since(), events_for_date(), purge_old_events()
|
||||
│ ├── compact/ — Compaction engine
|
||||
│ │ ├── mod.rs 317 lines ✅ — CompactionEngine trait, DefaultCompactionEngine, Clock, compaction orchestration, 1 test
|
||||
│ │ ├── config.rs 153 lines ✅ — CompactionConfig, AgentCompactionConfig, CompactionStrategyKind
|
||||
│ │ ├── plan.rs 150 lines ✅ — CompactionPlan, CompactionReport, AuditEntry, AuditFrontmatter
|
||||
│ │ └── strategy.rs 679 lines ✅ — 4 strategies: Microcompact, SlidingWindow, Summary, Cull + 10 tests
|
||||
│ ├── identity/ — Seed identity
|
||||
│ │ ├── mod.rs 3 lines ✅ — Re-exports (SeedId, glyph_from_pubkey)
|
||||
│ │ └── seed.rs 187 lines ✅ — Ed25519 keypair, load-or-generate, sign/verify, glyph rendering, 5 tests
|
||||
│ ├── credentials.rs 56 lines ✅ — CredentialStore trait, KeyringStore, get_bifrost_key()
|
||||
│ ├── sensorium/mod.rs 154 lines ⚠️ — Interface trait, BandwidthClass, DiscoveryLevel — trait exists, not wired to any backend
|
||||
│ ├── reflection/mod.rs 409 lines ✅ — N+25: 5-phase LLM pass, tool loop, ledgers, reflect_now(), exit_cleanly tracking
|
||||
│ ├── chain/mod.rs 50 lines ❌ — ChainOrchestrator stub (Talking vs Thinking) — only struct + new()
|
||||
│ └── subagent/mod.rs 25 lines ❌ — SubagentPool stub — only struct + new()
|
||||
│
|
||||
├── bridge/ — LLM provider
|
||||
│ ├── mod.rs 9 lines ✅ — Re-exports
|
||||
│ ├── bifrost.rs 361 lines ✅ — Chat completion, streaming, tool-calling, retry+backoff, InferenceStrain events
|
||||
│ └── model_router.rs 237 lines ✅ — Model selection, token counting, context pressure
|
||||
│
|
||||
├── server/ — HTTP server
|
||||
│ ├── mod.rs 228 lines ✅ — SouveraineServer: new(), run(), instance registry, compaction wiring
|
||||
│ ├── agent_inventory.rs 514 lines ✅ — Agent CRUD, SQLite, per-agent seed init, subconscious auto-creation, instance heartbeat
|
||||
│ ├── session_manager.rs 111 lines ✅ — Session create/get/list, SSE subscribers, add_message, conversation store wiring
|
||||
│ ├── conversation.rs 72 lines ⚠️ — ServerConversation: simplified Bifrost call, no tool loop (tool loop lives in LocalBackend)
|
||||
│ ├── consciousness_engine.rs 677 lines ✅ — N+1 Aster tool loop (5 rounds), N+25 reflection trigger, compaction warnings, heuristic fallback
|
||||
│ ├── energy.rs 151 lines ✅ — Energy balance: per-agent energy topology, generative/consumptive ratio, hot/cold desire tracking, heartbeat sync
|
||||
│ ├── gitea_memory.rs 115 lines ⚠️ — Gitea-backed memory (opt-in, graceful disable)
|
||||
│ ├── gitea_client.rs 150 lines ⚠️ — Gitea HTTP API client
|
||||
│ └── db.rs 77 lines ✅ — SQLite init, schema, migrations (agents, agent_instances tables)
|
||||
│
|
||||
├── api/ — REST API
|
||||
│ ├── mod.rs 65 lines ✅ — Route definitions (agents, conversations, memory CRUD, web static)
|
||||
│ ├── auth.rs 42 lines ✅ — Bearer-token auth middleware
|
||||
│ ├── handlers.rs 261 lines ✅ — All agent + conversation + memory handlers
|
||||
│ └── models.rs 190 lines ✅ — Request/response types (AgentState, AgentSummary, CreateAgentRequest, etc.)
|
||||
│
|
||||
├── ui/ — Terminal UI (legacy, partially superseded)
|
||||
│ ├── mod.rs 12 lines ✅ — Re-exports
|
||||
│ ├── app.rs 366 lines ✅ — TUI app (splash, menu, dashboard) — presence mode added
|
||||
│ ├── chat.rs 1582 lines ✅ — Full chat screen: bubbles, tool cards, cockpit, overlays, markdown rendering, streaming, surfacing
|
||||
│ ├── cockpit_panel.rs 197 lines ✅ — Aster observations panel (Surfacing/Reflection/Archivist/CompactionWarning)
|
||||
│ ├── health_panel.rs 169 lines ✅ — Health vitals pane: context pressure, N+1/N+25/N+100 cadence, inference strain tallies, compaction warnings, uptime
|
||||
│ ├── atmosphere.rs 129 lines ✅ — Visual presets: 14 color themes, posture-linked defaults, lerp transitions, outfit rendering
|
||||
│ ├── schedules.rs 439 lines ✅ — Schedules editor (Browse/Create/Delete/Enable-Disable/Run-now)
|
||||
│ ├── portrait.rs 143 lines ✅ — Per-agent portrait loading from agent memfs assets/
|
||||
│ ├── presence.rs 55 lines ✅ — Presence mode (breathing indicator in agent portrait)
|
||||
│ ├── component.rs 65 lines ✅ — Component trait, TuiEvent enum
|
||||
│ ├── markdown.rs 74 lines ✅ — Inline markdown renderer (bold, code, lists, headings)
|
||||
│ ├── color_support.rs 42 lines ✅ — Terminal color detection
|
||||
│ └── animation.rs 144 lines ✅ — Breathing, pulse, gradient, typewriter
|
||||
│
|
||||
├── tui/ — Terminal UI (new, slot-based)
|
||||
│ ├── mod.rs 5 lines ✅ — Re-exports
|
||||
│ ├── state.rs 102 lines ✅ — Application state, navigation, screens
|
||||
│ ├── components/ — Reusable widgets (skeletons)
|
||||
│ │ ├── mod.rs 30 lines ✅ — Re-exports
|
||||
│ │ ├── messages.rs 25 lines ⚠️ — Message list skeleton
|
||||
│ │ ├── input.rs 25 lines ⚠️ — Input area skeleton
|
||||
│ │ └── sidebar.rs 25 lines ⚠️ — Sidebar skeleton
|
||||
│ └── screens/ — Screen definitions
|
||||
│ ├── mod.rs 25 lines ✅ — Re-exports
|
||||
│ └── chat.rs 25 lines ⚠️ — "Coming Soon" stub
|
||||
│
|
||||
└── harness/mod.rs 68 lines ⚠️ — SouveraineHarness (stubbed — not the primary path)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Key Metrics
|
||||
|
||||
| Metric | Value |
|
||||
|--------|-------|
|
||||
| Total lines of Rust | ~13,000 |
|
||||
| Source files | 68 |
|
||||
| Working modules | 35+ |
|
||||
| Partial modules | 5 |
|
||||
| Stubbed modules | 3 (chain, subagent (pool), harness) |
|
||||
| TODO comments | 15+ |
|
||||
|
||||
## Module Dependency Graph
|
||||
|
||||
```
|
||||
main.rs
|
||||
├── cli::commands → backend, config
|
||||
├── backend::local → server::*, core::*, bridge::*, core::identity, core::nervous
|
||||
├── backend::remote → bridge::bifrost
|
||||
│
|
||||
├── core::config — standalone
|
||||
├── core::session — standalone
|
||||
├── core::memory → config
|
||||
├── core::subconscious → memory
|
||||
├── core::prompt → memory, skills
|
||||
├── core::skills — standalone
|
||||
├── core::tools → memory, compact, nervous (optional EventBus)
|
||||
├── core::nervous — standalone (EventBus channel)
|
||||
├── core::compact → bridge::model_router, bridge::bifrost, memory, session, config
|
||||
├── core::identity — standalone (ed25519-dalek)
|
||||
├── core::credentials — standalone (keyring)
|
||||
├── core::reflection → bridge::bifrost, tools, server::agent_inventory, session
|
||||
├── core::sensorium — standalone
|
||||
├── core::chain — standalone (stub)
|
||||
├── core::subagent — standalone (stub)
|
||||
│
|
||||
├── bridge::bifrost → core::session
|
||||
├── bridge::model_router → config
|
||||
│
|
||||
├── server::* → bridge, core::compact, core::memory, core::prompt, core::skills, core::identity
|
||||
├── api::* → server modules
|
||||
├── ui::* → backend, bridge, config (chat.rs wired async)
|
||||
└── tui::* → backend
|
||||
```
|
||||
|
||||
## Module States
|
||||
|
||||
| Module | LOC | State | Deps | Description |
|
||||
|--------|-----|-------|------|-------------|
|
||||
| config.rs | 659 | ✅ Full | none | TOML/YAML config loading, 16 sections, all defaults |
|
||||
| memory/ | 1032 | ✅ Full | config | Git-backed MemFS, 8 subcommands, frontmatter, auto-commit, limit enforcement, ledgers |
|
||||
| session/ | 68 | ✅ Full | none | Message types: Text, ToolUse, ToolResult, Reasoning |
|
||||
| conversation/ | 215 | ✅ Full | session | Event-sourced persistence, ConversationStore |
|
||||
| subconscious/ | 362 | ✅ Full | memory | 3-box inbox (pending/intrusive/sent), inner voice, urgency routing |
|
||||
| prompt/ | 408 | ✅ Full | memory, skills | Memfs-driven system prompt assembly, Aster prompt, ledger orientation |
|
||||
| skills/ | 365 | ✅ Full | none | 4-tier discovery, SKILL.md frontmatter, shadow resolution |
|
||||
| tools/ | 319 | ✅ Full | memory, compact, nervous | Sensorium registry, 11 tools, global lazy singleton |
|
||||
| nervous/ | 560 | ✅ Full | none | EventBus, CronSensor, HeartbeatHandler, EventLog (broadcast channel) |
|
||||
| compact/ | 1150 | ✅ Full | session, bridge, memory | CompactionEngine, 4 strategies, audit trail |
|
||||
| identity/ | 190 | ✅ Full | none | Ed25519 SeedId, sign/verify, glyph rendering |
|
||||
| credentials/ | 56 | ✅ Full | none | OS keyring + env var fallback |
|
||||
| reflection/ | 409 | ✅ Full | bridge, tools, server | N+25: 5-phase LLM pass, tool loop, ledger writes |
|
||||
| chain/ | 50 | ❌ Stub | none | Only struct + new() exists |
|
||||
| subagent/ | 25 | ❌ Stub | none | Only struct + new() exists |
|
||||
| sensorium/ | 154 | ⚠️ Partial | none | Interface trait, types — not wired to any backend |
|
||||
| bridge/bifrost/ | 361 | ✅ Full | session | HTTP client, chat completion, tool support |
|
||||
| bridge/model_router/ | 237 | ✅ Full | none | Model selection, tikToken, context pressure |
|
||||
| server/mod.rs | 228 | ✅ Full | bridge, config, compact | SouveraineServer, new(), run() with axum |
|
||||
| agent_inventory/ | 514 | ✅ Full | db, identity | Agent CRUD, SQLite, seed init, instance registry, subconscious creation |
|
||||
| session_manager/ | 111 | ✅ Full | conversation | Sessions, SSE subscribers, persistence |
|
||||
| consciousness_engine/ | 677 | ✅ Full | bridge, tools, subconscious, reflection | N+1 Aster tool loop, N+25 trigger, compaction warnings |
|
||||
| conversation/ | 72 | ⚠️ Minimal | bridge, session | Simplified Bifrost call, no tool loop |
|
||||
| gitea_memory/ | 115 | ⚠️ Partial | config | Gitea HTTP memory (gracefully disabled if unreachable) |
|
||||
| db/ | 77 | ✅ Full | none | SQLite init, schema, migrations |
|
||||
| api/ | 65 | ✅ Full | server | Route definitions (public, memory, web) |
|
||||
| api/auth/ | 42 | ✅ Full | none | Bearer-token auth middleware |
|
||||
| handlers/ | 261 | ✅ Full | server, models | All agent + conversation + memory handlers |
|
||||
| models/ | 190 | ✅ Full | none | Request/response types |
|
||||
| cli/ | 560 | ✅ Full | backend, config, identity | Subcommand dispatch |
|
||||
| backend/ | 114 | ✅ Full | none | Backend trait, 17 BackendEvent variants |
|
||||
| local/ | 927 | ✅ Full | server, nervous, tools | LocalBackend: full tool loop, SubagentRunner, TurnInjector |
|
||||
| remote/ | 134 | ✅ Full | bridge | RemoteBackend: reqwest + SSE |
|
||||
| ui/chat.rs | 1582 | ✅ Full | backend, bridge, config | Full chat screen: bubbles, tool cards, cockpit, overlays, markdown |
|
||||
| ui/cockpit_panel.rs | 197 | ✅ Full | none | Aster observations panel |
|
||||
| ui/schedules.rs | 439 | ✅ Full | nervous/cron | Schedules editor: browse/create/delete/toggle/run-now |
|
||||
| ui/portrait.rs | 143 | ✅ Full | none | Per-agent portrait from memfs assets/ |
|
||||
| ui/presence.rs | 55 | ✅ Full | none | Breathing presence indicator |
|
||||
| ui/markdown.rs | 74 | ✅ Full | none | Inline markdown renderer |
|
||||
| tui/state.rs | 102 | ✅ Full | none | App state, navigation, screens |
|
||||
| tui/components/ | 105 | ⚠️ Basic | state | Message list, input, sidebar skeletons |
|
||||
| tui/screens/ | 50 | ⚠️ Basic | state | Chat screen stubbed |
|
||||
| harness/ | 68 | ⚠️ Stub | none | SouveraineHarness (not the primary path) |
|
||||
23
saf/per-agent-compaction-provider.md
Normal file
23
saf/per-agent-compaction-provider.md
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
# Per-agent provider resolution in compaction engine
|
||||
|
||||
**Status:** pending
|
||||
|
||||
Compaction currently resolves via `ProviderRegistry::default_provider()`
|
||||
(`src/core/compact/mod.rs:154,181`). It doesn't have access to
|
||||
`AgentInventory` to load `AgentState` for per-agent resolution — it uses
|
||||
closure-based dependency injection (`get_messages`, `replace_messages`,
|
||||
`get_repo`, `get_agent_type`).
|
||||
|
||||
## What needs doing
|
||||
|
||||
Add a `get_agent: Arc<dyn Fn(&str) -> Option<AgentState> + Send + Sync>`
|
||||
closure (or pass `Arc<AgentInventory>` directly) so the Summary and
|
||||
SlidingReflect strategy branches can call
|
||||
`providers.for_agent(&agent_state)` instead of
|
||||
`providers.default_provider()`.
|
||||
|
||||
## Why
|
||||
|
||||
Agents routed through a non-default provider (e.g. Vanguard → z.ai) will
|
||||
fall back to Bifrost during compaction, which may fail if Bifrost's
|
||||
virtual key doesn't permit the agent's model.
|
||||
121
saf/plan.md
Normal file
121
saf/plan.md
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
# SAF: Execution Plan
|
||||
|
||||
> Ordered by impact, sequenced for "real harness today."
|
||||
> **Last updated:** 2026-05-12 (Full audit — several stages now complete, new tasks emerged)
|
||||
|
||||
---
|
||||
|
||||
## Overview — What Shipped Since May 6
|
||||
|
||||
The following was **all completed between May 6 and May 12**, across ~6,000 new lines of Rust:
|
||||
|
||||
- Stage 3 — TUI Chat: full wired chat with bubbles, tool cards, cockpit (thinking + subconscious panes), schedules editor, portraits, presence mode, markdown rendering
|
||||
- Stage 4 — LocalBackend: in-process engine with auto-fallback, SubagentRunner, TurnInjector, CronSensor/HeartbeatHandler wiring
|
||||
- Stage 5A — Memory module: git-backed MemFS with 8 subcommands, frontmatter, auto-commit, `limit` enforcement, ledgers
|
||||
- Stage 5B — N+1 Aster pass: full Bifrost tool loop (5 rounds), subconscious agent identity, ledger orientation, 4-fold mandate prompt
|
||||
- Stage 5C — N+25 Reflection engine: 5-phase LLM pass with tool access, wired at every 25th turn
|
||||
- Stage 5D — Compaction engine: 4 strategies (Microcompact/SlidingWindow/Summary/Cull), audit trail
|
||||
- Stage 5E — Skills system: 4-tier discovery, injected into system prompt
|
||||
- Stage 5F — Nervous system: EventBus, CronSensor, HeartbeatHandler, EventLog
|
||||
- Stage 5G — Seed identity: Ed25519 per-agent keypair + CLI subcommand
|
||||
- Stage 5H — Credentials: OS keyring + env var fallback
|
||||
- Stage 5I — Auth middleware: bearer-token API protection for memory routes
|
||||
- Stage 5J — Conversation persistence: event-sourced disk store with load/save/archive
|
||||
|
||||
---
|
||||
|
||||
## What's Still TODO (Priority Order)
|
||||
|
||||
### P1 — N+100 Archivist (Missing)
|
||||
|
||||
**Files to create/modify:**
|
||||
- `src/core/archivist/mod.rs` — NEW: rebuild the Archivist module post-cleanup
|
||||
- `src/server/consciousness_engine.rs` — Wire N+100 pass instead of placeholder pressure check
|
||||
|
||||
The original `core::archivist/` was removed from compilation (May 6) and never rebuilt. At 0.7+ pressure, the consciousness engine emits a `ConsciousnessEvent::Archivist` with a generic "Context compression triggered" string. No actual synthesis, no LLM call. The `ArchivistConfig` (enabled, interval, threshold, compression_model, synthesis_elements) exists in config but nothing reads it.
|
||||
|
||||
**Target architecture:** Smaller/faster model (e.g. `qwen2.5-7b`) calls Bifrost with a synthesis prompt, writes structured output (Themes/Emotions/Tensions/Anchors/Evolution/Patterns) to `system/synthesized/{date}.md`. Raw journal entries stay in git forever.
|
||||
|
||||
### P1 — N+1 After Heartbeat Turns
|
||||
|
||||
**File:** `src/core/nervous/handler.rs`
|
||||
|
||||
`TurnInjector::inject_background_turn` in LocalBackend drains the stream silently but never calls `ConsciousnessEngine::on_response` after the turn completes. This means scheduled turns from the cron system never get Aster analysis. The fix is to hook into the post-turn processing path so heartbeat-injected turns also pass through the subconscious.
|
||||
|
||||
### P2 — Remote Git Sync (push/pull)
|
||||
|
||||
**Files:** `src/core/memory/mod.rs`
|
||||
|
||||
Neither `push()` nor `pull()` exist on `MemoryRepo`. The real agent's `.git/config` may already have a remote configured. The `auto_push` config option exists but is never read. Needed for: off-machine backup, letting Ani on the home server sync memory to a remote, federation foundation.
|
||||
|
||||
### P2 — Per-Agent Context Limit Fix
|
||||
|
||||
**Status:** 🟡 Partially resolved
|
||||
|
||||
`ConsciousnessEngine` now looks up the agent's `llm_config.context_window` (commit `79a23bb`). The `context_limit` field is correctly read per-agent. What's still hardcoded: the `128_000` fallback in `pressure_for_session()` when `agents.get()` fails. No per-agent compaction thresholds (all agents share the global 80/90/95% tiers).
|
||||
|
||||
### P2 — Subagent Pool (Lifecycle Manager)
|
||||
|
||||
**Files:** `src/core/subagent/mod.rs` (25 lines, stub)
|
||||
|
||||
The `SubagentPool` exists as a struct with `new()`. No lifecycle tracking, no concurrency limits, no heartbeat monitoring. The LocalSubagentRunner handles individual spawns correctly but there's no central pool to:
|
||||
- Limit concurrent forks
|
||||
- Track parent-child relationships
|
||||
- Kill orphaned subagents
|
||||
- Report pool health
|
||||
|
||||
### P3 — Sensorium Unification
|
||||
|
||||
**Files:** `src/core/sensorium/mod.rs` (154 lines, trait + types only)
|
||||
|
||||
Sensorium trait exists with BandwidthClass (High/Medium/Low/Minimal) and DiscoveryLevel. Not wired into any backend. Would enable bandwidth-aware SSE events, progressive UI discovery, and context-adaptive interaction. The TuiSensorium, MobileSensorium, and Coordinator from CLAUDE.md are not built.
|
||||
|
||||
### P3 — Chain Orchestrator (Talking vs Thinking)
|
||||
|
||||
**Files:** `src/core/chain/mod.rs` (50 lines, stub)
|
||||
|
||||
ChainOrchestrator was supposed to manage Talking (reactive, fast) vs Thinking (reflective, slow) chains. Never implemented beyond struct + new().
|
||||
|
||||
### P4 — Federation Transport
|
||||
|
||||
**Files:** `saf/atproto-federation.md` (research), `src/core/nervous/mod.rs` (seed_id pre-wired)
|
||||
|
||||
WebSocket bridge between EventBus instances on different machines. ATProto-ready DID identity model. The `seed_id` field on `SensorEvent` is pre-wired as the federation identity marker. Not started.
|
||||
|
||||
### P4 — OSSUI Integration
|
||||
|
||||
**File:** `src/api/mod.rs` (web routes)
|
||||
|
||||
Server has `web/dist/` SPA fallback wired. No actual UI built there. Rebrand ex-letta-oss-ui as Souveraine web interface.
|
||||
|
||||
### P4 — Cloister Diff-Canary
|
||||
|
||||
**Location:** Not yet scoped
|
||||
|
||||
N+1 should read `git diff` of the last commit and validate against domain schema. HALT/WARN/LOG severity. From `docs/CONSCIOUSNESS_CYCLE.md` § Cloister Security Model.
|
||||
|
||||
---
|
||||
|
||||
## Remaining Design Decisions
|
||||
|
||||
These were deferred in prior sessions and are **still unresolved**:
|
||||
|
||||
1. **Archivist model selection:** Should N+100 use a dedicated model (config: `archivist.compression_model`), or should the agent pick her own compression model? The `auto` value in config defaults means "let the system decide" — no algorithm written for that yet.
|
||||
|
||||
2. **N+1 model for Aster:** `openai/glm-5.1-precision` is the current default hardcoded in reflection.rs line 130. Should this be driven by config only (it is in `SubconsciousConfig.model` for the engine, but the reflection engine has its own fallback chain)? Current resolution: the consciousness engine passes the config value through; the reflection engine's hardcoded default is a fallback.
|
||||
|
||||
3. **Cloister canary severity:** HALT vs WARN vs LOG on domain schema violation? Not designed. The current subconscious pass never inspects git diff.
|
||||
|
||||
4. **lifetime_active_seconds granularity:** Currently ticks in 30s buckets. Useful for uptime percent but not fine-grained enough for "how long has this agent been running this session." No per-session wall-clock tracking.
|
||||
|
||||
---
|
||||
|
||||
## Resumption Checklist
|
||||
|
||||
When a new model picks up:
|
||||
- [ ] Read `saf/plan.md` for current priorities
|
||||
- [ ] Check `docs/tasks/` for active task documents
|
||||
- [ ] Read `CLAUDE.md` for conventions (substrate, not harness)
|
||||
- [ ] Start with P1 items: Archivist (N+100) or Heartbeat N+1 wiring
|
||||
- [ ] Do NOT add `--force-compact` or `auto_compact_at_threshold` knobs
|
||||
- [ ] Update `saf/plan.md` with progress
|
||||
102
saf/server.md
Normal file
102
saf/server.md
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
# SAF: Server Implementation
|
||||
|
||||
> HTTP server layer — axum on port 8484.
|
||||
> **Last updated:** 2026-05-12 (Full audit — memory CRUD, auth, instance registry)
|
||||
|
||||
---
|
||||
|
||||
## Implementation Status
|
||||
|
||||
| Area | Module | Status | Notes |
|
||||
|------|--------|--------|-------|
|
||||
| Server struct + lifecycle | `server/mod.rs` (228 lines) | ✅ | SouveraineServer, new(), run(), instance registry, compaction wiring |
|
||||
| Database schema | `server/db.rs` (77 lines) | ✅ | agents + agent_instances tables, SQLite |
|
||||
| API models | `api/models.rs` (190 lines) | ✅ | All request/response types |
|
||||
| Session manager | `server/session_manager.rs` (111 lines) | ✅ | Sessions with SSE broadcast, conversation store wiring |
|
||||
| Agent inventory | `server/agent_inventory.rs` (514 lines) | ✅ | CRUD + SQLite + per-agent seed init + subconscious creation + instance heartbeat |
|
||||
| HTTP handlers | `api/handlers.rs` (261 lines) | ✅ | Agent, conversation, memory handlers |
|
||||
| API routes | `api/mod.rs` (65 lines) | ✅ | Public + memory + web routes |
|
||||
| Auth middleware | `api/auth.rs` (42 lines) | ✅ | Bearer-token protection for memory routes |
|
||||
| Consciousness engine | `server/consciousness_engine.rs` (677 lines) | ✅ | N+1 Aster (full LLM tool loop), N+25 reflection, 3-tier compaction warnings |
|
||||
| CLI integration | `main.rs` + `cli/commands.rs` | ✅ | `souveraine server` command |
|
||||
| Gitea memory | `server/gitea_memory.rs` (115 lines) | ⚠️ | Opt-in, gracefully disabled if unreachable |
|
||||
| Server conversation | `server/conversation.rs` (72 lines) | ⚠️ | Simplified Bifrost call, no tool loop (tool loop lives in LocalBackend) |
|
||||
|
||||
---
|
||||
|
||||
## What the Server Provides
|
||||
|
||||
### Endpoints
|
||||
|
||||
| Method | Path | Handler | Status |
|
||||
|--------|------|---------|--------|
|
||||
| GET | `/health` | health_check | ✅ |
|
||||
| GET | `/v1/agents` | list_agents | ✅ |
|
||||
| POST | `/v1/agents` | create_agent | ✅ (auto-creates subconscious + seed) |
|
||||
| GET | `/v1/agents/:id` | get_agent | ✅ |
|
||||
| PATCH | `/v1/agents/:id` | update_agent | ✅ |
|
||||
| DELETE | `/v1/agents/:id` | delete_agent | ✅ |
|
||||
| GET | `/v1/conversations` | list_conversations | ✅ |
|
||||
| POST | `/v1/conversations` | create_conversation | ✅ |
|
||||
| GET | `/v1/conversations/:id` | get_conversation | ✅ |
|
||||
| POST | `/v1/conversations/:id/messages` | stream_messages (SSE) | ✅ |
|
||||
| GET | `/v1/agents/:id/memory` | list_memory | ✅ (auth required) |
|
||||
| GET | `/v1/agents/:id/memory/*path` | read_memory | ✅ (auth required) |
|
||||
| PUT | `/v1/agents/:id/memory/*path` | write_memory | ✅ (auth required) |
|
||||
| PATCH | `/v1/agents/:id/memory/*path` | append_memory | ✅ (auth required) |
|
||||
| DELETE | `/v1/agents/:id/memory/*path` | delete_memory | ✅ (auth required) |
|
||||
| GET | `/` | ServeDir(`web/dist/`) | ✅ (SPA fallback, no UI built) |
|
||||
|
||||
### Endpoints Not Implemented
|
||||
|
||||
No Letta-compatible block endpoints (`/v1/agents/:id/core-memory/blocks`) are planned — Souveraine committed to memfs-only memory per `docs/MEMORY_BLOCKS_DECISION.md`. The memory CRUD endpoints above replace Letta's block API.
|
||||
|
||||
### Instance Registry
|
||||
|
||||
The server maintains an `agent_instances` table (`server/db.rs`, `server/agent_inventory.rs`):
|
||||
- `register_instance()` — creates one row per known agent per process, prunes stale rows (>5 min)
|
||||
- `heartbeat_instance()` — bumps `last_seen_at` and increments `lifetime_active_seconds` in 30s ticks
|
||||
- `instance_count()` — how many running instances for a given agent
|
||||
- `lifetime_active_seconds()` — total lifecycle uptime for uptime percentage
|
||||
|
||||
A background tokio task in `SouveraineServer::new()` handles the 30s heartbeat loop.
|
||||
|
||||
### Auth
|
||||
|
||||
Memory routes require a bearer token (`Authorization: Bearer <token>`), enforced by middleware at `api/auth.rs`. Loopback requests (127.0.0.1 / ::1) can bypass auth when `auth.allow_loopback` is true (configurable in `souveraine.toml` `[server.auth]` section). Public routes (agents list, conversations, health) are unauthenticated.
|
||||
|
||||
---
|
||||
|
||||
## How to Run
|
||||
|
||||
```bash
|
||||
souveraine server
|
||||
# Binds to 127.0.0.1:8484 (configurable: [server] bind=, port=, or SOUVERAINE_SERVER_BIND env)
|
||||
# Creates ~/.souveraine/server/
|
||||
# ├── agents/ # Agent directories with UUID naming
|
||||
# │ └── {uuid}/
|
||||
# │ ├── agent.json
|
||||
# │ └── conversations/
|
||||
# └── database.sqlite3 # Agent index + instance registry
|
||||
|
||||
# With custom bind
|
||||
souveraine server --bind 0.0.0.0 --port 8484
|
||||
```
|
||||
|
||||
The user-side memfs lives at `~/.souveraine/agents/{uuid}/memory/` — this is the single canonical path. Subconscious agents at `~/.souveraine/subconscious-agents/{id}-sub/`.
|
||||
|
||||
---
|
||||
|
||||
## Architecture Note
|
||||
|
||||
The server and LocalBackend share the same engine (`SouveraineServer`). The difference is the transport layer:
|
||||
- **Server mode**: axum HTTP + SSE — client/server separation
|
||||
- **Local mode**: in-process `Arc<SouveraineServer>` with direct `BackendEvent` streaming — no socket
|
||||
|
||||
Both paths run the same `run_turn()` tool loop (in `LocalBackend`), the same N+1 Aster pass, and the same N+25 reflection trigger. The server's `ServerConversation` (72 lines) is a simplified single-turn path used only by the SSE handler; the full tool loop with all 11 tools lives in `LocalBackend::run_turn()`.
|
||||
|
||||
---
|
||||
|
||||
## Debugging
|
||||
|
||||
There is currently no `souveraine server` debug output — `run()` uses `println!` not tracing. Memory CRUD endpoints are logged via auth middleware.
|
||||
14
saf/tui-model-picker-provider.md
Normal file
14
saf/tui-model-picker-provider.md
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
# Expose per-agent provider in TUI settings model-picker
|
||||
|
||||
**Status:** pending
|
||||
|
||||
The TUI settings screen (`src/ui/screens/settings/`) lets you pick an
|
||||
agent's model but doesn't expose `_souveraine.provider`. An agent routed
|
||||
through z.ai can only be configured via hand-editing agent.json + DB.
|
||||
|
||||
## What needs doing
|
||||
|
||||
- Add a provider field/picker in the settings UI (likely alongside the
|
||||
model picker in `model_picker.rs` or `field_grid.rs`)
|
||||
- Populate options from `ProviderRegistry`'s known provider names
|
||||
- Write through to `agent.json` and `database.sqlite3 config_json`
|
||||
18
saf/update-agent-request-souveraine.md
Normal file
18
saf/update-agent-request-souveraine.md
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
# Carry _souveraine block through UpdateAgentRequest API
|
||||
|
||||
**Status:** pending
|
||||
|
||||
`UpdateAgentRequest` (the API endpoint for live agent edits) doesn't
|
||||
carry the `_souveraine` block. So `provider`, `subconscious_model`,
|
||||
`reflection_model`, and `archivist_model` can't be set via the API —
|
||||
only via hand-editing `agent.json` and restarting the server.
|
||||
|
||||
## What needs doing
|
||||
|
||||
- Add `_souveraine` fields to `UpdateAgentRequest` in
|
||||
`src/api/models.rs`
|
||||
- Wire them through `AgentInventory::update()` so they write to both
|
||||
`agent.json` and `database.sqlite3 config_json`
|
||||
- The existing update path already skips `config_json` rewrites
|
||||
(`src/server/agent_inventory.rs:459-469`) — that gap needs filling
|
||||
regardless
|
||||
Loading…
Reference in a new issue