Skip to main content

norpagent

Copyright (c) 2026 xingluosama121, MIT Licensed

norpagent is a registry-based, pluggable component framework for building agents.

Models, tools, session management, sandbox environments, task scheduling, context stores, user interfaces, and external plugins are all independent components resolved by name through a registry. Developers can replace or combine components without modifying framework core code.

Core concepts

+--------------------------------------------------------------+
|                           Registry                           |
|   models / tools / sessions / sandboxes / schedulers / UI /  |
|                     plugins / components                     |
+--------------------------------------------------------------+
|                            Preset                            |
|   Declarative config: component selection + behavior params, |
|                    defining a run mode                       |
+--------------------------------------------------------------+
|                   AgentRuntime (generic loop)                |
|   Decoupled from specific models/tools: message build ->     |
|           model call -> tool execution -> backfill           |
+--------------------------------------------------------------+
  • Models: registry.register_model("my_model", MyModelProvider()), referenced by name in presets
  • Tools: registry.register_tool("my_tool", MyTool()); register a whole plugin with registry.register_plugin(MyPlugin())
  • Replaceable components: session stores, sandboxes, task schedulers, UI adapters, context stores, project managers
  • Generic component namespace: registry.register_component(kind, name, factory); presets declare components={"kind": "name"}

Installation

pip install norpagent                  # Core package, no third-party dependencies (Mock model + built-in tools)
pip install norpagent[openai]          # OpenAI-compatible model adapter (OpenAI/DeepSeek/Qwen/vLLM/Ollama)
pip install norpagent[anthropic]       # Claude series model adapter
pip install norpagent[web]             # Web access enhancement (requests + bs4; stdlib engine used when not installed)
pip install norpagent[security]        # Plugin signature verification (cryptography; without it signatures are treated as untrusted)
pip install norpagent[all]             # All optional dependencies

Model adapters and tools are registered with the core package and lazy-loaded by the SDK: missing extras do not affect registration or mode listing; an install hint is shown only when the component is actually invoked.

Quick start (np() entry point, since v0.5)

import norpagent as np

np()                        # standard preset + Web frontend (all built-in tools)
running = True
while running:
    if np.stop() == True:   # lifecycle function: exit when the application ends
        running = False
    else:
        time.sleep(0.05)    # poll gently (Ctrl+C interrupts this wait instantly)

After startup the console prints [norpagent] listening on http://127.0.0.1:8787/; open that address in a browser to get the chat UI (front.html: multi-tab sessions / streaming rendering / settings / plugin panel). The UI language can be set with np(language="zh_CN") (default: English).

  • The default preset is standard: the model can use all built-in tools (files / commands / web / context management / project management / long-running task collaboration). Without an API key the mock model is used automatically; enter a model and key in the UI "Settings" to switch to a real model;
  • "Settings -> Reasoning effort" maps to the model's reasoning_effort (passed through for OpenAI reasoning models and DeepSeek V4; DeepSeek V4 transmits the chain-of-thought reasoning_content separately from the body text, and the kernel passes it back verbatim on rounds where tool calls occurred; selecting "off" in the UI translates to DeepSeek V4's thinking=disabled). When reasoning is enabled the temperature parameter is disabled automatically;
  • The "Project root" default follows the OS (Windows: Documents\NORP-Agent; macOS: ~/Documents/NORP-Agent; Linux: ~/norpagent-workspace); the "Browse" button opens a directory picker (navigation via a read-only backend listing).

Web UI behavior and persistence:

  • 模块流程编排(FLOW):Web UI 顶栏的「🧩 流程编排」按钮(或直接访问 /flow)打开模块流程画布——模块是方块、注册的钩子是端口、beam 连线即 执行链路。RUN 会把画布图提交给后端用注册表真实组件拓扑执行 (模型 / 工具 / 沙箱 / 插件 / 安全),进度经 SSE 实时回显;核心模块坞按 注册表快照渲染,拖入 .py 文件即走完整安全管线真实注册(一个钩子 = 一个节点);工具/插件等任意模块可装入工具容器(成员在 TB 下方 从上到下排列,容器外侧端口 = 成员端口并集 + 自身端口)。 详见 docs/flow.md
  • Settings persistence: model / API key / language / plugin directory settings are persisted to ~/.norpagent/webui_config.json after saving in the browser (overridable with the NORPAGENT_WEBUI_CONFIG environment variable); they survive page refreshes and np() process restarts;
  • Page cache prevention: page responses carry Cache-Control: no-store;
  • Client disconnect handling: browser refresh / tab close / curl interruption are handled silently without tracebacks in the console;
  • Port fallback: if 8787 is taken, up to 10 subsequent ports are tried; the console prints the actually bound port;
  • Request body guards: negative Content-Length is treated as no body; request bodies over 1MB are rejected;
  • Event routing: kernel session ids match browser tab ids; reasoning / reply / task-done events are delivered to the corresponding session;
  • Ctrl+C interruption: Ctrl+C raises KeyboardInterrupt promptly (Windows-safe poll-wait on the loop instead of a single blocking Event.wait()); in-flight tasks are cancelled cooperatively — the PTC sandbox kills its child process, the pooled sandbox kills the process tree, model streams abort — and the process exits without hanging (daemon worker pool; no ThreadPoolExecutor exit-join).

Architecture slots + address functions: apart from the low-level minimal kernel (ArchLayer / address resolution / registry / event bus), all components are slots. No address = default logic; an address = plug in an implementation:

np(preset="standard")                                  # preset mode
np(model="openai_compat")                              # model
np(async_loop="myapp.loop:create")                     # event loop system
np(frontend="norpagent.frontends.console:ConsoleFrontend")  # console frontend
np(frontend="norpagent.frontends.headless:HeadlessFrontend")# headless (output printed to stdout)
np(port=9000, language="zh_CN")                        # Web port and UI language
np(html="/path/to/my.html")                            # custom main page: file path or HTML content
np(frontend="norpagent.frontends.web:WebFrontend;html=/path/to/my.html")  # same via address subclause
np(session="sqlite", sandbox="pooled", security="high")# component replacement
loop = np.nasyncio("myapp.loop:create")                # standalone architecture function for the loop system

Hot remount: any slot can be replaced while the engine is running:

np.remount(model="openai_compat")                      # swap model: effective on the next run()
np.remount(tools=["echo", "get_time"])                 # swap tool set
np.remount(session="sqlite")                           # swap session store: AgentRuntime hot-rebuilt
np.remount(frontend="norpagent.frontends.console:ConsoleFrontend")  # stop old / start new frontend
np.remount(model="myapp.model:create")                 # edit the module file, remount = hot reload

Component slots (model / tools / hooks / security / plugins) take effect on the next run; assembly slots (session / sandbox / scheduler / ui / agent_runtime / preset / context_store / project_manager) trigger an AgentRuntime hot rebuild; frontend / async_loop stop-old-start-new. String addresses invalidate module and .pyc caches before re-resolving, and re-mounted architecture-level subscriptions are unsubscribed first (no duplicate firing). See docs/DEVELOPER_MANUAL.md §3.7.

Single task (headless mode, output printed to stdout):

np(prompt="Summarize the README", preset="standard")
while True:
    if np.stop():
        break
    time.sleep(0.05)
print(np.current().last_result.final_content)

See docs/DEVELOPER_MANUAL.md for the developer manual.

Quick start (manual assembly)

from norpagent import Registry, AgentRuntime
from norpagent.builtin import install_defaults
from norpagent.modes import register_all_presets

reg = Registry()
install_defaults(reg)          # register built-in models/tools/sessions/sandboxes/schedulers/UI/components
register_all_presets(reg)      # register standard / ptc / minimal / creative presets

agent = AgentRuntime(reg, preset="minimal")
result = agent.run("hello")
print(result.final_content)

Command line:

python -m norpagent --list-modes        # list all preset modes
python -m norpagent --mode minimal      # interactive REPL in minimal mode
python -m norpagent --mode standard --model mock --prompt "..."   # single task (mock model)
python -m norpagent --mode standard --model-name deepseek-v4-flash --base-url https://api.deepseek.com/v1
python -m norpagent --mode ptc --prompt "Call the echo tool from code" --model mock
python -m norpagent --mode-file my_mode.py   # load a custom mode file
python -m norpagent --mode standard --session sqlite --call-timeout 60   # persistent session + hard timeout
python -m norpagent --mode standard --ui web --port 8787      # Web UI (HTTP + SSE event stream)
python -m norpagent --mode standard --plugin-dir ./my_plugins  # load external plugins (signature -> audit -> import restriction)
python -m norpagent plugin-sign --gen                        # generate a plugin signature keypair
python -m norpagent plugin-sign my_plugin.py --key <private-key-hex>  # sign a plugin (generates .sig)

Preset modes

Mode Component combination
minimal mock/openai models + echo/clock tools (deterministic environment for model benchmarking)
standard openai_compat model + file/command/web/context/project/task tools (persistent sessions + long-running task scheduling)
ptc openai_compat model + run_python + business tools (PTC: the model generates Python code combining multi-step tool calls)
creative mock model + basic tools, arbitrary overrides allowed (supports --mode-file custom mode files)

Built-in component catalog

Model adapters

Name Description
mock Deterministic scripted-response model (benchmarking / debugging, no third-party dependencies)
openai_compat OpenAI-compatible services: OpenAI / DeepSeek / Qwen / vLLM / Ollama (norpagent[openai])
anthropic Claude series (norpagent[anthropic])

Tools

Name Description
echo / get_time Basic tools (minimal mode)
run_python PTC execution core: code runs isolated in a sandbox subprocess (AST precheck + restricted builtins + clean namespace + hard kill on timeout), call_tool() composes multi-step tool calls over the protocol channel
file_read / file_write / file_list / file_delete File operations: confined to the workspace root; absolute paths and .. traversal rejected
exec_cmd Command execution: goes through the sandbox protocol (replace the sandbox implementation to replace the execution environment)
web_search / web_fetch / web_extract_links Web access: SSRF protection (private/loopback addresses rejected), requests+bs4 preferred with stdlib fallback
context_add / context_search / context_list / context_delete Context management: cross-session searchable knowledge base (FTS5 + BM25, mixed Chinese/English tokenization)
project_status Project management: file statistics, recent changes, git branch and change awareness
task_submit / task_list / task_status / task_cancel Long-running task collaboration: subtasks queued by priority, multi-agent orchestration, resume after interruption

Sessions / sandboxes / scheduling / UI / components

Name Description
memory / sqlite Session stores: in-process / SQLite persistence (default ~/.norpagent/sessions.db)
subprocess / pooled Sandboxes: one-shot subprocess / pooled (reuse + concurrency cap + hard-kill the entire process tree on timeout)
simple / persistent Task scheduling: in-process FIFO / SQLite-persistent priority queue (resume() continues after a crash)
console / web UI: console / Web (HTTP + SSE event stream, no third-party dependencies)
context_store=fts5 Context store component (default ~/.norpagent/context.db)
project_manager=basic Project management component (workspace .norpagent/project.json metadata)

Context management

Agents write intermediate conclusions, external material, and code snippets from long-running tasks into the context store for cross-session, cross-task retrieval:

from norpagent.builtin.context import FTS5ContextStore

store = FTS5ContextStore()                    # default ~/.norpagent/context.db
store.add("The network module falls back between the requests and urllib engines", source="notes", title="network")
hits = store.search("network engine")         # BM25 relevance ranking, mixed Chinese/English tokenization

After a preset declares components={"context_store": "fts5"}, tools access the store via ctx.context_store; to replace the implementation, register another component factory - no core code changes needed.

Task scheduling and multi-agent collaboration

from norpagent.builtin.scheduler.persistent import PersistentTaskScheduler
from norpagent.protocols.scheduler import AgentTask

sched = PersistentTaskScheduler()             # tasks persisted to disk, survive process restarts
sched.submit(AgentTask(id="", user_input="Organize documents", params={"priority": 0}))
sched.submit(AgentTask(id="", user_input="Run tests", params={"priority": 5}))
sched.resume()                                # crash recovery: leftover running tasks re-queued

agent = AgentRuntime(reg, preset="standard")
results = sched.drain(agent.task_runner())    # execute by priority; subtasks may specify a different preset

Models submit subtasks via the task_submit tool; task.preset_name selects a different mode, i.e. a different sub-Agent (sharing the same registry and component store).

External plugins

from norpagent.plugins import install_plugin_dirs

loader = install_plugin_dirs(registry, ["my_plugins"], config={
    "plugin_security_audit": "warn",          # off / warn / block
    "plugin_security_import_restrict": "off", # off / safe / strict
    "plugin_signature_verify": True,          # Ed25519 verification (norpagent[security])
    "plugin_network_policy": "deny",          # deny / audited_public / public_only / allow_all
})
for info in loader.plugins:
    print(info.name, info.signature_status, info.enabled)

Plugin format: module-level PLUGIN_NAME / TOOLS (OpenAI function schemas) / execute(tool_name, args, ctx) / 15 lifecycle hooks / APPROVAL_HINTS.

Plugin loading security pipeline: discovery -> signature verification (invalid rejected) -> AST audit (critical rejected, including getattr / dict reflection bypass detection) -> permission declaration check -> import restriction (static precheck + runtime meta_path interception) -> adapter registration. Trusted signatures relax the audit level.

PluginSystem facade and process-level isolation:

from norpagent.plugins import PluginSystem

ps = PluginSystem(registry, ["my_plugins"], config={"plugin_isolation": "auto"})
ps.load(); ps.status(); ps.reload("my_tool"); ps.shutdown()

Module-level ISOLATION = "process" enables process-level plugin isolation: plugin code is loaded and executed only inside a host subprocess (python -m norpagent.plugins.host, JSON-lines RPC protocol); tools and hooks are forwarded over RPC, crashes restart automatically, hook execution is time-limited. Every pipeline stage (discovery/load/audit/register) is a hook; HookVeto can abort the current stage.

9 layers, 29 hooks (norpagent.hooks)

Execution structures are exposed as hook APIs and can be intervened on:

from norpagent.hooks import before_model_call, after_tool_call, HookVeto, HookLayer

before_model_call.subscribe(logger, system=reg)        # module-level subscription
agent.hooks.after_tool_call.subscribe(observer)        # runtime-bound view

def forbid_rm(event):                                   # mutating hook: raise HookVeto to abort the current execution structure
    if event.get("tool_name") == "file_delete":
        raise HookVeto("deleting files is forbidden")
agent.hooks.before_tool_call.subscribe(forbid_rm)

net = HookLayer("L10_network", order=100)              # custom layer + custom hook
net.hook("before_network_call", mutating=True)
agent.hooks.install_layer(net)

Nine layers: L1 runtime lifecycle -> L2 task -> L3 input -> L4 session and history -> L5 message assembly -> L6 step -> L7 model call -> L8 tool call -> L9 result finalization. Mutating hooks can rewrite the data flow (input/messages/arguments/results); see docs/hooks.md.

Security system entry point: norpagent.safe()

from norpagent import safe

kit = safe(reg, level="standard")     # enable security components by level (basic/standard/high)
kit.scan_input(text)                  # jailbreak/injection detection
kit.harden(prompt, tools)             # prompt hardening
kit.audit_file(path)                  # AST audit
kit.verify_plugin(path)               # signature verification
kit.check_network(url)                # SSRF verdict

Security components (guard/hardening/approval/audit/signature/network policy/isolation policy) are provided uniformly by norpagent.safe(): input guard = L3 hook, prompt hardening = L5 hook, the remaining runtime policies = registry.security; the kernel does not depend on norpagent.security directly. See docs/security.md.

Security guards (norpagent.security)

from norpagent.security import (
    SourceAuditor,       # AST source audit: dangerous calls/imports/dynamic bypasses
    NetworkPolicy,       # plugin network policy + SSRF protection
    ApprovalPolicy,      # human approval: native tool confirmation + plugin tool approval
    scan_message,        # jailbreak/prompt injection detection (DAN/role override/zero-width chars/Base64)
    SignatureVerifier,   # Ed25519 plugin signature verification
)

Kernel integration (opt-in):

  • params["jailbreak_guard"] = True: block jailbreak/injection before user input reaches the model;
  • params["harden_prompt"] = True: append security hardening rules to the system prompt;
  • params["approval_policy"] / params["approval_config"]: human approval before tool calls (via UI ask_user; user denial blocks the call).

Preset definition example

from norpagent.kernel.presets import Preset

MY_PRESET = Preset(
    name="my_mode",
    description="custom mode",
    model="openai_compat",           # any registered model name
    tools=["file_read", "exec_cmd"], # any registered tool names
    session="sqlite",                # session store (memory / sqlite)
    sandbox="pooled",                # sandbox (subprocess / pooled)
    scheduler="persistent",          # task scheduling (simple / persistent)
    ui="web",                        # user interface (console / web)
    components={                     # generic component assembly
        "context_store": "fts5",
        "project_manager": "basic",
    },
    params={
        "max_steps": 32,
        "temperature": 0.7,
        "task_timeout": 0,           # task timeout in seconds (checked at round boundaries, 0 = unlimited)
        "call_timeout": 60,          # hard timeout per model call in seconds (kills a blocked call, 0 = unlimited)
        "workspace_root": ".",       # workspace root for file tools
        "jailbreak_guard": True,     # jailbreak/injection interception
        "harden_prompt": True,       # system prompt hardening
    },
)

Timeouts (two levels)

  • task_timeout: task-level timeout, checked at round boundaries (the model call is synchronous and blocking);
  • call_timeout: model-call-level hard timeout, mapped to the model API request timeout. On timeout the main loop stops waiting and returns a timeout result; the background request thread is marked cancelled (params["_cancel_event"], letting the adapter's streaming loop exit early) and is reclaimed in shutdown().

Plugin protocol (in-process)

class MyPlugin:
    name = "my_plugin"
    version = "1.0.0"
    publisher = "me"

    def get_tools(self):            # register tools with the Agent
        return [EchoTool()]

    def get_hooks(self):            # subscribe to Agent lifecycle events
        return {"on_task_done": self.on_done}

    def on_done(self, event):
        print("task done:", event.payload)

External (file) plugins are loaded through the norpagent.plugins loader and pass the signature verification / audit / import restriction / network policy / human approval security pipeline.

Security behavior

  • Workspace path locking: file tools require paths relative to the workspace root; absolute paths, .. traversal, and symlink escapes are rejected;
  • SSRF protection: web tools only allow public http/https; after DNS resolution loopback/private/link-local addresses are rejected;
  • Sandbox pool hard kill: command timeout kills the entire process tree (Windows taskkill /T, POSIX process-group SIGKILL); killed instances are not reused;
  • Plugin security pipeline: signature -> AST audit (including reflection bypass detection) -> permission declaration -> import restriction;
  • Jailbreak guard: DAN/role override/restriction-removal pattern matching + zero-width characters + Unicode homoglyphs + Base64 hidden instruction detection;
  • PTC restricted execution: run_python builtin whitelist + imports disabled.

Documentation

Document Contents
docs/hooks.md 9 layers, 29 hooks: semantics, mutating hook return contracts, custom layers/hooks
docs/security.md norpagent.safe(): three preset levels, SafetyKit API, kernel boundary
docs/presets.md standard/ptc/minimal/creative presets
docs/plugins.md Plugin system: security pipeline, PluginSystem facade, process-level isolation

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

norpagent-0.7.0.tar.gz (397.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

norpagent-0.7.0-py3-none-any.whl (447.8 kB view details)

Uploaded Python 3

File details

Details for the file norpagent-0.7.0.tar.gz.

File metadata

  • Download URL: norpagent-0.7.0.tar.gz
  • Upload date:
  • Size: 397.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.12.10

File hashes

Hashes for norpagent-0.7.0.tar.gz
Algorithm Hash digest
SHA256 573a5ec6e52480b6697609f10acaa7a09e18f77d7fd3b4062fda5ea0d5d85638
MD5 8a91670392dc7e848fecd79ece5d00f9
BLAKE2b-256 d4741df2732f98ff4f619c529a46e84f624abb042076064eb77aceb21696ee64

See more details on using hashes here.

File details

Details for the file norpagent-0.7.0-py3-none-any.whl.

File metadata

  • Download URL: norpagent-0.7.0-py3-none-any.whl
  • Upload date:
  • Size: 447.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.12.10

File hashes

Hashes for norpagent-0.7.0-py3-none-any.whl
Algorithm Hash digest
SHA256 71693abacfee34d67a9a94b8c26f32648567baba8a394a837dfc26f3f8c0c28a
MD5 cd7e2ef9b4ecb09a20d8979eb5a20737
BLAKE2b-256 0ce2829d864af72b3e92845d26a79176e5e0acc984865653b12ee1a293bfe5a2

See more details on using hashes here.

Release history Release notifications | RSS feed

2.0.0

2 files

1.0.4

2 files

1.0.3

2 files

1.0.2

2 files

1.0.1

2 files

0.9.9

2 files

0.9.8

2 files

0.9.7

2 files

0.9.5

2 files

0.9.4

2 files

0.9.1

2 files

0.9.0

2 files

This release

0.7.0 This release

2 files

0.6.9

2 files

0.6.2

2 files

0.5.1

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page