zettcode-engine 0.3.4__tar.gz → 0.4.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/PKG-INFO +1 -1
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/engine/core.py +1 -1
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/engine/plugins/context.py +9 -0
- zettcode_engine-0.4.1/src/zettcode/engine/plugins/graph.py +96 -0
- zettcode_engine-0.4.1/src/zettcode/engine/plugins/loader.py +635 -0
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/engine/plugins/plugin.py +9 -0
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/engine/plugins/switches.py +10 -2
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/plugin_tui.py +2 -2
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/version.py +1 -1
- zettcode_engine-0.3.4/src/zettcode/engine/plugins/loader.py +0 -367
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/.gitignore +0 -0
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/pyproject.toml +0 -0
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/_compat.py +0 -0
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/budget.py +0 -0
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/config.py +0 -0
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/config_edit.py +0 -0
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/engine/__init__.py +0 -0
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/engine/agent/__init__.py +0 -0
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/engine/agent/base.py +0 -0
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/engine/agent/context.py +0 -0
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/engine/agent/exports.py +0 -0
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/engine/agent/interaction.py +0 -0
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/engine/agent/sessions.py +0 -0
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/engine/agent/settings.py +0 -0
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/engine/agent/turns.py +0 -0
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/engine/dialogue/__init__.py +0 -0
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/engine/dialogue/ask.py +0 -0
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/engine/dialogue/side.py +0 -0
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/engine/dialogue/title.py +0 -0
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/engine/export/__init__.py +0 -0
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/engine/export/trace/script.js +0 -0
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/engine/export/trace/style.css +0 -0
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/engine/export/trace/template.html +0 -0
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/engine/extensions/__init__.py +0 -0
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/engine/extensions/capabilities.py +0 -0
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/engine/extensions/compaction.py +0 -0
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/engine/extensions/context.py +0 -0
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/engine/extensions/files.py +0 -0
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/engine/extensions/guidance.py +0 -0
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/engine/extensions/harness.py +0 -0
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/engine/extensions/harness_switch.py +0 -0
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/engine/extensions/mcp_notice.py +0 -0
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/engine/extensions/relay.py +0 -0
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/engine/extensions/subagents.py +0 -0
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/engine/extensions/tool_output.py +0 -0
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/engine/extensions/truncation.py +0 -0
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/engine/extensions/usage.py +0 -0
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/engine/harness_paths.py +0 -0
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/engine/permission/__init__.py +0 -0
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/engine/permission/approval.py +0 -0
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/engine/permission/readonly.py +0 -0
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/engine/permission/sandbox.py +0 -0
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/engine/pipeline.py +0 -0
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/engine/plugins/__init__.py +0 -0
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/engine/plugins/commands.py +0 -0
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/engine/plugins/extension.py +0 -0
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/engine/plugins/groups.py +0 -0
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/engine/plugins/mixins.py +0 -0
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/engine/plugins/services.py +0 -0
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/engine/plugins/settings.py +0 -0
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/engine/plugins/state.py +0 -0
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/engine/py.typed +0 -0
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/engine/server_tools.py +0 -0
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/engine/storage/__init__.py +0 -0
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/engine/storage/layout.py +0 -0
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/engine/storage/ledger.py +0 -0
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/engine/storage/locks.py +0 -0
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/engine/storage/metadata.py +0 -0
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/engine/storage/records.py +0 -0
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/engine/storage/store.py +0 -0
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/engine/tools/__init__.py +0 -0
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/engine/tools/boundary.py +0 -0
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/engine/tools/files.py +0 -0
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/engine/tools/images.py +0 -0
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/engine/tools/output.py +0 -0
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/engine/tools/shell.py +0 -0
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/engine/tools/workspace.py +0 -0
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/error_text.py +0 -0
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/filelock.py +0 -0
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/logging_setup.py +0 -0
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/mention.py +0 -0
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/modes.py +0 -0
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/paths.py +0 -0
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/processes.py +0 -0
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/prompt.py +0 -0
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/providers.py +0 -0
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/py.typed +0 -0
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/registry.py +0 -0
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/relay.py +0 -0
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/statusline_parts.py +0 -0
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/wire.py +0 -0
- {zettcode_engine-0.3.4 → zettcode_engine-0.4.1}/src/zettcode/wording.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: zettcode-engine
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.1
|
|
4
4
|
Summary: The agent core of ZettCode: the runtime over zett-agent, and the leaf types it is written on
|
|
5
5
|
Project-URL: Repository, https://github.com/Chang-LeHung/zettcode
|
|
6
6
|
Author: Chang-LeHung
|
|
@@ -106,7 +106,7 @@ def _load_engine_plugins(config: ZettCodeConfig, shared: Mapping[str, object]) -
|
|
|
106
106
|
compaction, the MCP client) is :mod:`zettcode.sdk.builtins`, which passes its own loader here. A runtime built
|
|
107
107
|
this way still honours ``[plugins]`` for the distributions and single files a reader installed.
|
|
108
108
|
"""
|
|
109
|
-
return load_plugins(config, builtins=(), shared=shared)
|
|
109
|
+
return load_plugins(config, builtins=(), shared=shared, agent=False)
|
|
110
110
|
|
|
111
111
|
|
|
112
112
|
def build_system_prompt(config: ZettCodeConfig, *, now: datetime | None = None) -> str:
|
|
@@ -196,6 +196,15 @@ class PluginContext:
|
|
|
196
196
|
"""Return the object available under ``name``, or ``None`` when there is none (see :meth:`service`)."""
|
|
197
197
|
return lookup({**self._available, **self._services}, name, kind)
|
|
198
198
|
|
|
199
|
+
def inject(self, name: str, service: object) -> object:
|
|
200
|
+
"""Make an object available to the other plugins under ``name``; the same as :meth:`provide`.
|
|
201
|
+
|
|
202
|
+
A plugin that lists this one in ``deps`` is activated after it and finds the object with :meth:`service`.
|
|
203
|
+
What the object is, and how it is used, is for the plugins to agree on: the loader only keeps the name
|
|
204
|
+
unique and the order right.
|
|
205
|
+
"""
|
|
206
|
+
return self.provide(name, service)
|
|
207
|
+
|
|
199
208
|
def provide(self, name: str, service: object) -> object:
|
|
200
209
|
"""Register an object under a name, so other parts of the application can look it up.
|
|
201
210
|
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"""Put plugins in dependency order, and say which of them cannot be loaded at all.
|
|
2
|
+
|
|
3
|
+
A plugin names the plugins it needs in :attr:`~zettcode.engine.plugins.plugin.Plugin.deps`. :func:`order` turns those
|
|
4
|
+
names into a topological order — a plugin is always activated after every plugin it needs — and into the reasons a
|
|
5
|
+
plugin has to be left out: a plugin it needs is absent, or the needs go round in a circle. The order is stable: among
|
|
6
|
+
the plugins that are ready, the one that was offered first goes first, so a graph with no edges keeps exactly the
|
|
7
|
+
order it was given in, and the built-in plugins stay ahead of the installed ones unless one of them needs a later one.
|
|
8
|
+
|
|
9
|
+
The module knows names and edges only, not plugins, so it is one pure function that a test can feed strings.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
from dataclasses import dataclass
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@dataclass(frozen=True, slots=True)
|
|
18
|
+
class Node:
|
|
19
|
+
"""One plugin as the graph sees it: the name it is known by and the names it needs."""
|
|
20
|
+
|
|
21
|
+
name: str
|
|
22
|
+
deps: tuple[str, ...] = ()
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def order(nodes: list[Node]) -> tuple[list[int], dict[int, str]]:
|
|
26
|
+
"""Return the activation order of ``nodes`` and the reason each node that cannot be activated has.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
nodes: The candidates, in the order they were offered.
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
The indices of the nodes that can be activated, dependencies first, and ``{index: why}`` for the ones that
|
|
33
|
+
cannot. A node that needs a failed node fails too, so the reasons name the first thing missing and the chain
|
|
34
|
+
it took down is in the later reasons. A name offered twice belongs to its first node; the second is ordered
|
|
35
|
+
like any other and left for the loader to refuse as a duplicate.
|
|
36
|
+
"""
|
|
37
|
+
owner: dict[str, int] = {}
|
|
38
|
+
for index, node in enumerate(nodes):
|
|
39
|
+
owner.setdefault(node.name, index)
|
|
40
|
+
|
|
41
|
+
failed: dict[int, str] = {}
|
|
42
|
+
changed = True
|
|
43
|
+
while changed:
|
|
44
|
+
changed = False
|
|
45
|
+
for index, node in enumerate(nodes):
|
|
46
|
+
if index in failed:
|
|
47
|
+
continue
|
|
48
|
+
for dep in node.deps:
|
|
49
|
+
if dep == node.name:
|
|
50
|
+
failed[index] = f"plugin {node.name!r} lists itself in deps"
|
|
51
|
+
elif dep not in owner:
|
|
52
|
+
failed[index] = f"needs plugin {dep!r}, which is not installed or is turned off"
|
|
53
|
+
elif owner[dep] in failed:
|
|
54
|
+
failed[index] = f"needs plugin {dep!r}, which cannot be loaded"
|
|
55
|
+
else:
|
|
56
|
+
continue
|
|
57
|
+
changed = True
|
|
58
|
+
break
|
|
59
|
+
|
|
60
|
+
alive = [index for index in range(len(nodes)) if index not in failed]
|
|
61
|
+
waiting = {index: {owner[dep] for dep in nodes[index].deps if owner[dep] != index} for index in alive}
|
|
62
|
+
ordered: list[int] = []
|
|
63
|
+
done: set[int] = set()
|
|
64
|
+
remaining = list(alive)
|
|
65
|
+
while remaining:
|
|
66
|
+
ready = next((index for index in remaining if waiting[index] <= done), None)
|
|
67
|
+
if ready is None:
|
|
68
|
+
break
|
|
69
|
+
ordered.append(ready)
|
|
70
|
+
done.add(ready)
|
|
71
|
+
remaining.remove(ready)
|
|
72
|
+
|
|
73
|
+
for index in remaining:
|
|
74
|
+
cycle = _cycle(index, nodes, owner, set(remaining))
|
|
75
|
+
failed[index] = (
|
|
76
|
+
f"dependency cycle: {' -> '.join(cycle)}" if cycle else "needs a plugin that is part of a dependency cycle"
|
|
77
|
+
)
|
|
78
|
+
return ordered, failed
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def _cycle(start: int, nodes: list[Node], owner: dict[str, int], within: set[int]) -> list[str]:
|
|
82
|
+
"""Return the names on a path from ``start`` back to itself inside ``within``, or ``[]`` when it is on none."""
|
|
83
|
+
stack: list[tuple[int, list[int]]] = [(start, [start])]
|
|
84
|
+
seen: set[int] = set()
|
|
85
|
+
while stack:
|
|
86
|
+
index, path = stack.pop()
|
|
87
|
+
for dep in nodes[index].deps:
|
|
88
|
+
target = owner.get(dep)
|
|
89
|
+
if target is None or target not in within:
|
|
90
|
+
continue
|
|
91
|
+
if target == start:
|
|
92
|
+
return [nodes[step].name for step in (*path, start)]
|
|
93
|
+
if target not in seen:
|
|
94
|
+
seen.add(target)
|
|
95
|
+
stack.append((target, [*path, target]))
|
|
96
|
+
return []
|