Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
136 commits
Select commit Hold shift + click to select a range
efebe73
chore: add gitignore
surafelfikru Mar 26, 2026
3820880
feat: add telegram channel
surafelfikru Mar 26, 2026
7e7224a
feat: implement telegram calls in metta
surafelfikru Mar 26, 2026
8051e38
feat(telegram): add telegram channel
surafelfikru Mar 26, 2026
5c0cac1
feat(telegram): implement telegram calls in metta
surafelfikru Mar 26, 2026
910a4f7
chore(telegram): remove global declarations
surafelfikru Mar 30, 2026
96ad4a5
feat(telegram): change last_message to a queue
surafelfikru Mar 30, 2026
31e7c3f
Implemented capability matrix
blackhammer116 Mar 30, 2026
2efbc53
Revert "feat(telegram): change last_message to a queue"
surafelfikru Mar 30, 2026
69f2fb2
Merge branch 'main' into telegram
surafelfikru Mar 30, 2026
f5bf87c
feat(telegram): set default group chat id
surafelfikru Mar 30, 2026
93ad7dc
feat(telegram): add group chat
surafelfikru Mar 30, 2026
4a27c11
chore(telegram): update last_message to handle multiple messages
surafelfikru Mar 30, 2026
c0c09c5
Merge 'telegram' to telegram-sec
surafelfikru Apr 1, 2026
5a2f7e0
Merge branch 'main' into telegram
surafelfikru Apr 2, 2026
2880219
Merge pull request #1 from blackhammer116/telegram-sec
surafelfikru Apr 2, 2026
41d5847
Feat: Changed framework to aiogram and enforced security and ethics h…
blackhammer116 Apr 2, 2026
270700a
chore: added some blocker for memory
blackhammer116 Apr 2, 2026
b8473ed
feat: change useMiniMax to asiCloud
surafelfikru Apr 2, 2026
966413d
Merge branch 'telegram' of github.com:iCog-Labs-Dev/mettaclaw into te…
surafelfikru Apr 2, 2026
b2a74b3
chore: revert history to original
surafelfikru Apr 2, 2026
a8a7fc7
Merge branch 'main' into telegram
surafelfikru Apr 3, 2026
aa008e5
Merge branch 'main' into telegram-pre-sec
surafelfikru Apr 3, 2026
5aff0e2
Fix: fixed permission error and premature exit of main loop
blackhammer116 Apr 3, 2026
947c174
Fix: Fixed permission issue and premature exit
blackhammer116 Apr 3, 2026
ffc76a9
Move Telegram credentials to .env and refactor channel dispatch
surafelfikru Apr 3, 2026
040a5b2
Add interactive configuration prompts at startup
surafelfikru Apr 3, 2026
fc281dc
feat: add Dockerfile and firewall.sh
surafelfikru Apr 3, 2026
d0e9b1d
Merge remote-tracking branch 'origin/telegram' into telegram
blackhammer116 Apr 3, 2026
f57346c
Fix: fixed last message retrival and profile/policy dir error
blackhammer116 Apr 5, 2026
5ffeeef
chore: removed some things
blackhammer116 Apr 5, 2026
07f791d
chore: set cmake build parallelizm to 2
surafelfikru Apr 5, 2026
8325c8e
Merge branch 'telegram' of github.com:iCog-Labs-Dev/mettaclaw into te…
surafelfikru Apr 5, 2026
3f9b36e
chore: update prompt.txt
surafelfikru Apr 5, 2026
6441326
chore: clear history
surafelfikru Apr 5, 2026
c55a5dc
chore(prompt): update prompt to be less idle
surafelfikru Apr 5, 2026
41ac6ab
feat(telegram): add feature to reply to mentions only
surafelfikru Apr 5, 2026
1491f92
Feat: Implemented several security features
blackhammer116 Apr 6, 2026
d5b70c2
Feat: Hardened rate limiting security and enforced batch processing
blackhammer116 Apr 6, 2026
b07e3e6
chore: cleared history
blackhammer116 Apr 6, 2026
970234b
Merge branch 'telegram-pre-sec' into telegram-sec
blackhammer116 Apr 6, 2026
74d2aaf
Fix: solved bot token error
blackhammer116 Apr 7, 2026
8c8b342
removed unwanted file
blackhammer116 Apr 8, 2026
e868ed7
chore: remove ptb dependency
surafelfikru Apr 8, 2026
45bea5d
Refactor: replace prompt-configure with configure and remove dead code
surafelfikru Apr 8, 2026
4249b94
Fix: resolve config path relative to script location, minor cleanups
surafelfikru Apr 8, 2026
27c0617
Fix: resolve isRequired paths dynamically for Docker compatibility
surafelfikru Apr 8, 2026
3d676b0
Fix: pre-create telegram log file with mettauser ownership in Dockerfile
surafelfikru Apr 8, 2026
2431625
Fix: Added a simple prompt to make it stop spamming
blackhammer116 Apr 9, 2026
bf88d24
Feat: fixed the ethics and safety system to be more accurate and less…
blackhammer116 Apr 9, 2026
e2d5146
Fix: repair broken f-strings that span multiple lines
surafelfikru Apr 9, 2026
3fe7449
Merge remote telegram-sec: per-chat batching, LLM ethics, admin commands
surafelfikru Apr 9, 2026
c946c9d
chore: remove explicit 60 seconds mention in system prompt
surafelfikru Apr 9, 2026
8f42a5f
Merge pull request #4 from blackhammer116/telegram-sec
surafelfikru Apr 9, 2026
9c73699
Merge branch 'telegram-pre-sec' into telegram
surafelfikru Apr 9, 2026
1aed494
Fix: Removed batching
blackhammer116 Apr 10, 2026
3b16c58
chore: added dynamic admin identification
blackhammer116 Apr 10, 2026
24f9ec1
chore: set default search setting to true
surafelfikru Apr 11, 2026
20bb561
Fix: fixed search issue and moved ethics pass inside the tg_channels …
blackhammer116 Apr 12, 2026
087734d
Merge branch 'telegram' into telegram
surafelfikru Apr 12, 2026
73fe59b
Merge pull request #5 from blackhammer116/telegram
surafelfikru Apr 12, 2026
2337433
chore: update prompt and add to knowledge-base
surafelfikru Apr 12, 2026
020247a
chore: add volumens on Dockerfile for memory
surafelfikru Apr 13, 2026
ef30e21
feat: add RAG based knowledge base indexing and query
surafelfikru Apr 13, 2026
9b46e5d
Feat: Enabled use of NAL reasoning in TG and added some markdown feat…
blackhammer116 Apr 13, 2026
9743608
Merge remote-tracking branch 'upstream/telegram' into telegram
blackhammer116 Apr 13, 2026
0fe6338
Feat: Added metta as usable skill
blackhammer116 Apr 13, 2026
a920e02
Fix: Restoring NAL to the original repr
blackhammer116 Apr 14, 2026
606ca27
Feat: Added skill save functionality
blackhammer116 Apr 14, 2026
d0fba2e
chore: update knowledge base from 'Oma-bot-kb-09_04_26'
surafelfikru Apr 14, 2026
639ccce
Feat: Updated docker removed guardrails for skills and added compose
blackhammer116 Apr 15, 2026
f78ddcc
added env in compose
blackhammer116 Apr 15, 2026
ff78b87
Merge remote-tracking branch 'core/main' into telegram
surafelfikru Apr 16, 2026
88e7a48
Chore: moddularized the configs and added comments
blackhammer116 Apr 17, 2026
bde72ff
Merge remote-tracking branch 'upstream/telegram' into telegram
blackhammer116 Apr 17, 2026
0e64913
Chore: removed unnecessary checks from skills and removed redundant file
blackhammer116 Apr 17, 2026
7e12edc
Merge remote-tracking branch 'origin/ref/server-sec' into telegram
blackhammer116 Apr 17, 2026
b67fc7d
Chore: Ensured full omegaclaw capability, fixed initialization of log…
blackhammer116 Apr 17, 2026
e6f7888
Chore: unified telegram config and removed unnecessary telegram check…
blackhammer116 Apr 17, 2026
0e61a09
Chore: unified vector db with rag
blackhammer116 Apr 17, 2026
47b8864
Feat: unified rag vector db with native LTM for better knowledge-base…
blackhammer116 Apr 17, 2026
af5e5b8
Chore: changed embedding provider back to local
blackhammer116 Apr 17, 2026
b36fdf7
Merge pull request #10 from blackhammer116/telegram
surafelfikru Apr 20, 2026
2aa51f6
Fix: Fixed fallback path and some reply prompt modifications
blackhammer116 Apr 20, 2026
1de125d
Chore: unified diverging changes from core
blackhammer116 Apr 20, 2026
478fedc
Merge pull request #11 from blackhammer116/telegram
surafelfikru Apr 20, 2026
bc2ac24
chore: remove logs and add aiogram to python dependecies
surafelfikru Apr 20, 2026
0da1afe
Feat: Enabled bot to bot comm with flag, integrated Patrick's fix for…
blackhammer116 Apr 22, 2026
e1fe954
Merge remote-tracking branch 'upstream/telegram' into telegram
blackhammer116 Apr 22, 2026
e4daacf
feat(telegram): stricter tool-use prompt and two-arg file-command par…
surafelfikru Apr 22, 2026
ffd8b81
Feat: Enabled persistent logging
blackhammer116 Apr 22, 2026
0033aa2
Merge remote-tracking branch 'upstream/telegram' into telegram
blackhammer116 Apr 22, 2026
ac474a4
Fix: added allowed chats flag for persistent blocking of external chats
blackhammer116 Apr 22, 2026
3cb6da1
Fix: Enforcing strict external chat block
blackhammer116 Apr 22, 2026
813ed3c
Fix: explicitly blocking other chats
blackhammer116 Apr 22, 2026
a795441
Fix: enabled admin dm and enforced admin checker
blackhammer116 Apr 22, 2026
e55a447
Chore: Updated policy
blackhammer116 Apr 22, 2026
3d7ffe5
Fix: fixing the quoting issue using asi/OmegaClaw-Core PR# 66
blackhammer116 Apr 22, 2026
5a10249
Feat: enabled multiple authorized chat functionality and fixed shell,…
blackhammer116 Apr 23, 2026
362235b
Merge remote-tracking branch 'core/main'
blackhammer116 May 7, 2026
22f34b7
Remove unwanted memory policy and configuration files to keep in sync…
blackhammer116 May 8, 2026
6ff32b7
Feat: Integrated single context frames
blackhammer116 May 12, 2026
5e4c417
Fix/Feat: Fixed read-file error, optimized frame passing to decrease …
blackhammer116 May 14, 2026
30bd10f
Merge remote-tracking branch 'core/main'
blackhammer116 Jun 2, 2026
3a40e3d
chore: added venv and log dir to gitignore
blackhammer116 Jun 2, 2026
214c5f6
Merge pull request #26 from blackhammer116/main
blackhammer116 Jun 2, 2026
4f714ed
Feat: Implemented Context-Frames V2
blackhammer116 Jun 22, 2026
0734766
Merge pull request #33 from blackhammer116/main
blackhammer116 Jun 22, 2026
f3fa299
Feat: added frame composition and frame management skills
blackhammer116 Jul 1, 2026
e4e3b02
Merge pull request #36 from blackhammer116/main
blackhammer116 Jul 1, 2026
be641b0
Fix: Relations are now properly captured via frameID and labled child…
blackhammer116 Jul 3, 2026
5e81ea4
Feat: integrated llm lib from core
blackhammer116 Jul 7, 2026
bd3bfec
Feat: Updated requirements file
blackhammer116 Jul 9, 2026
e069183
Merge branch 'iCog-Labs-Dev:main' into main
Bereket-Eshete Jul 16, 2026
d0784ff
fix(context): normalise quoted frame IDs in cfv2-load-frame and fix F…
Bereket-Eshete Jul 20, 2026
699ca61
feat(loop): classify into 4 frame-aware signal cases
Bereket-Eshete Jul 20, 2026
97534c2
fix(context,loop): address PR review feedback
Bereket-Eshete Jul 20, 2026
d6b1921
Merge pull request #52 from Bereket-Eshete/ref/context-frame
blackhammer116 Jul 20, 2026
9344882
Fix: exposed the last_admitted frame id to the root frame
blackhammer116 Jul 27, 2026
5bf93e5
chore: removed useFrame flag and legacy context builder
blackhammer116 Jul 28, 2026
0425508
Merge remote-tracking branch 'core/main'
blackhammer116 Jul 28, 2026
3f03d49
Fix: fixed auth rstrip error and added frame based skills to LLM and …
blackhammer116 Jul 28, 2026
67c21c9
Fix: fixed failing autotests
blackhammer116 Jul 28, 2026
b641d0c
Fix: fixed stale requests in autotest
blackhammer116 Jul 28, 2026
07bc8bd
chore: reverted autotests changes
blackhammer116 Jul 28, 2026
66d85e7
Feat: Separated context frame related prompt and base prompt with a f…
blackhammer116 Jul 29, 2026
51a8f0d
Merge remote-tracking branch 'core/main'
blackhammer116 Jul 29, 2026
956a639
[OMEGA-286] Match mock answers against the context frame
TossSky Jul 30, 2026
e5f066b
Feat: unified llm call as in lib_llm_ext
blackhammer116 Jul 30, 2026
4913be6
chore: Updated frame_relation.py to use text-embedding-3-large as the…
blackhammer116 Jul 30, 2026
edba683
Fix: prevent frame completion from hanging in relation search
blackhammer116 Jul 30, 2026
4636666
[OMEGA-286] Fix frame completion loop and multi-form answer parsing
TossSky Jul 31, 2026
cf7e7ed
Merge pull request #55 from sassless/qa/omega-286-frames-mock
blackhammer116 Jul 31, 2026
689bd83
Merge pull request #56 from sassless/fix/omega-286-frame-completion-loop
blackhammer116 Jul 31, 2026
c64e510
Merge remote-tracking branch 'core/main'
blackhammer116 Jul 31, 2026
a72837e
Merge remote-tracking branch 'upstream/main'
blackhammer116 Jul 31, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@ chroma_db/
# pulled in repos
repos/

# virtual environments
.venv/
venv/

# logs
*.log

# secrets
.env
memory/.channel/
Expand Down
10 changes: 10 additions & 0 deletions Autotests/mock/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,13 @@ def comm():
yield server
finally:
server.stop(5)


# Answers and the frames they were served for do not outlive the test that
# registered them, so a frame left unfinished by one test cannot answer for
# the next one.
@pytest.fixture(autouse=True)
def reset_llm(request):
yield
if "llm" in request.fixturenames:
request.getfixturevalue("llm").reset(5)
147 changes: 122 additions & 25 deletions Autotests/mock/llm.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,49 @@
except ImportError:
from rpc import Rpc, IPCClient, IPCServer
from contextlib import contextmanager
import re
import threading

LLM_MOCK_PORT = 9765

FRAME_SECTION_HEADER = "CURRENT_CONTEXT_FRAME_S_EXPR:"
FRAME_SECTION_END = "_newline_SKILLS:"
FRAME_COMPLETION_SKILLS = ("complete-goals-stm", "complete-goals-ltm", "clear-frame-junk")
FRAME_COMPLETION_ANSWER = '(complete-goals-stm "Answered by the mock, frame completed by the test harness.")'
FRAME_COMPLETION_ATTEMPTS = 2
FRAME_ID = re.compile(r"\(frameID\s+([^)\s]+)\)")


def unescape(text):
return (text
.replace("_apostrophe_", "'")
.replace("_quote_", '"')
.replace("_newline_", "\n"))


def frame_section(prompt):
start = prompt.find(FRAME_SECTION_HEADER)
if start < 0:
return None
start += len(FRAME_SECTION_HEADER)
end = prompt.find(FRAME_SECTION_END, start)
return prompt[start:] if end < 0 else prompt[start:end]


def frame_id(section):
found = FRAME_ID.search(section)
return found.group(1) if found else "unknown-frame"


class LlmMockAgent:

def __init__(self, address):
self._lock = threading.Lock()
self._answers = {}
self._served = {}
self._rpc = Rpc(IPCClient(address))
self._rpc.on_request('set_answer', lambda args: self.on_set_answer(args))
self._rpc.on_request('reset', lambda args: self.on_reset(args))
self._rpc.on_request('ping', lambda args: self.on_ping(args))
self._rpc.start()

Expand All @@ -28,57 +60,114 @@ def chat(self, content):
if len(user) < 2:
return ""

message = self._message(user[1])
if message is not None:
answer = self._message_answer(message)
if answer:
print(f"[LlmMockAgent] Mock answers: {answer}")
return answer

section = frame_section(user[0])
if section is not None:
return self._frame_answer(section)

if message is not None:
print(f"[LlmMockAgent] Mock doesn't have answer for: {message}")
return ""

def _message(self, suffix):
try:
body = eval(user[1])[1]
except SyntaxError:
return ""
return eval(suffix)[1]
except Exception:
return None

def _message_answer(self, body):
# The agent escapes punctuation that would confuse its s-exp
# parser ('->_apostrophe_, "->_quote_, \n->_newline_) before
# the text reaches chat(). set_answer stores the literal
# prompt key, so try the raw body first, then the normalized
# form so prompts with quotes/apostrophes/newlines still match.
def normalize(text):
return (text
.replace("_apostrophe_", "'")
.replace("_quote_", '"')
.replace("_newline_", "\n"))

with self._lock:
answer = self._answers.get(body) or self._answers.get(normalize(body))
answer = self._response(self._answers.get(body) or self._answers.get(unescape(body)))
if answer:
print(f"[LlmMockAgent] Mock answers: {answer}")
return answer

# IRC may deliver multiple PRIVMSGs in one agent iteration; the
# agent concatenates them with " | " between speakers. Split
# and look up each fragment individually so a registered answer
# is not missed when several messages arrive together.
fragments = body.split(" | ")
for fragment in fragments:
for fragment in body.split(" | "):
if ": " not in fragment:
continue
prompt = fragment.split(": ", 1)[1]
with self._lock:
a = self._answers.get(normalize(prompt)) or self._answers.get(prompt)
if a:
answer = a
found = self._answers.get(unescape(prompt)) or self._answers.get(prompt)
if found:
answer = self._response(found)

return answer

# With context frames the received message is no longer passed after
# the ":-:-:-:" delimiter: the delimiter carries a loop signal and the
# text becomes the current frame, projected into the prompt under
# CURRENT_CONTEXT_FRAME_S_EXPR. Matching against that section rather
# than the whole prompt keeps the mock honest: a message that was
# admitted but did not become current is not answered.
def _frame_answer(self, section):
current = unescape(section)
frame = frame_id(section)

if answer:
print(f"[LlmMockAgent] Mock answers: {answer}")
return answer
else:
print(f"[LlmMockAgent] Mock doesn't have answer for: {body}")
return ""
with self._lock:
match = None
for request, entry in self._answers.items():
if request in current or unescape(request) in current:
match = (request, entry)
break

if match is None:
print(f"[LlmMockAgent] Mock doesn't have answer for frame: {frame}")
return ""

request, (response, complete_frame) = match
served = self._served.get((frame, request), 0)
self._served[(frame, request)] = served + 1

# A frame outlives the iteration that answered it, so the answer is
# served once. While the same frame keeps coming back the harness
# completes it instead, otherwise the next message is admitted but
# never becomes current and every later test fails with it.
if served == 0:
if complete_frame and not any(skill in response for skill in FRAME_COMPLETION_SKILLS):
response = f"{response} {FRAME_COMPLETION_ANSWER}"
print(f"[LlmMockAgent] Mock answers: {response}")
return response

if complete_frame and served <= FRAME_COMPLETION_ATTEMPTS:
print(f"[LlmMockAgent] Frame {frame} is still current, completing it")
return FRAME_COMPLETION_ANSWER

print(f"[LlmMockAgent] Frame {frame} was already answered")
return ""

def _response(self, entry):
return entry[0] if entry else None

def on_set_answer(self, args):
with self._lock:
request = args['request']
response = args['response']
complete_frame = args.get('complete_frame', True)
print(f'[LlmMockAgent] Mock request: "{request}" with response "{response}"')
self._answers[request] = response
self._answers[request] = (response, complete_frame)
return True

def on_reset(self, args):
with self._lock:
self._answers.clear()
self._served.clear()
print('[LlmMockAgent] Mock answers cleared')
return True

def on_ping(self, args):
print(f'[LlmMockAgent] Mock ping request processed')
return True
Expand All @@ -92,13 +181,21 @@ def __init__(self, address):
def stop(self, timeout=None):
self._rpc.stop(timeout)

def set_answer(self, request, response, timeout=10):
result = self._rpc.request('set_answer', { 'request': request, 'response': response })
def set_answer(self, request, response, complete_frame=True, timeout=10):
result = self._rpc.request('set_answer', { 'request': request, 'response': response,
'complete_frame': complete_frame })
if result.get(timeout) != True:
print(f'[LlmMockController] Cannot set answer to the mock, error: {result.error()}')
return False
return True

def reset(self, timeout=10):
result = self._rpc.request('reset', {})
if result.get(timeout) != True:
print(f'[LlmMockController] Cannot reset the mock, error: {result.error()}')
return False
return True

def ping(self, timeout=None):
print(f'[LlmMockController] Ping agent')
result = self._rpc.request('ping', {})
Expand Down
129 changes: 129 additions & 0 deletions Autotests/mock/test_llm.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,27 @@

TEST_ADDRESS = (LOCALHOST, 9767)

FRAME = "Frame-20260729T144803343500Z"
OTHER_FRAME = "Frame-20260729T144839020270Z"
SIGNAL = "NEW_INPUT_HAS_BEEN_ADMITTED_AS_ACTIVE_FRAME."


def frame_prompt(deliverable, frame=FRAME, signal=SIGNAL):
return (
"PROMPT: You are a OmegaClaw agentic harness in a continuous loop._newline_"
"RUNTIME-PROMPT: The current frame is the authoritative task state._newline_"
"CURRENT_CONTEXT_FRAME_S_EXPR: (ContextProjection (RootFrame (RootFrame RootFrame-1 "
f"(current-frame-id {frame}) (last-admitted-frame-id {frame}) (mode Fast))) "
f"(CurrentFrame (Frame (frameID {frame}) (parent-frameID ()) (source UserDirective) "
"(priority 1.0) (status Active) (frame-mode Fast) "
f"(history-summary sha256:a48d311b2a4172dc chars:{len(deliverable)} excerpt:{deliverable}) "
f"(deliverables ({deliverable})) (results ()))))_newline_"
"SKILLS: - Remember a particular string: remember string_newline_"
"OUTPUT_FORMAT: Up to 5 lines_newline_"
"TIME: 2026-07-29 14:48:03"
f":-:-:-:{signal}"
)

class TestLlmMock:

def setup_class(cls):
Expand Down Expand Up @@ -64,3 +85,111 @@ def test_context_manager_timeout(self, agent):
assert False
except RuntimeError as e:
assert e.args == ("Agent didn't answered in 2 seconds",)

def test_iteration_without_message_is_silent(self, agent, controller, capsys):
assert controller.set_answer("hello", "world")
capsys.readouterr()

assert agent.chat("PROMPT: nothing new here:-:-:-:") == ""

assert "Mock doesn't have answer" not in capsys.readouterr().out

def test_missing_answer_is_reported_with_the_message(self, agent, controller, capsys):
assert controller.set_answer("hello", "world")
capsys.readouterr()

assert agent.chat(":-:-:-:['HUMAN-MSG', 'test: goodbye']") == ""

assert "Mock doesn't have answer for: test: goodbye" in capsys.readouterr().out

def test_reset_drops_answers(self, agent, controller):
assert controller.set_answer("hello", "world")
assert agent.chat(":-:-:-:['HUMAN-MSG', 'test: hello']") == "world"
assert controller.reset()
assert agent.chat(":-:-:-:['HUMAN-MSG', 'test: hello']") == ""


class TestLlmMockContextFrames:

@pytest.fixture
def agent(self):
agent = LlmMockAgent(TEST_ADDRESS)
yield agent
agent.stop(5)

@pytest.fixture
def controller(self):
controller = LlmMockController(TEST_ADDRESS)
yield controller
controller.stop(5)

def test_answer_matched_against_current_frame(self, agent, controller):
request = "[REQ-1] please write Hello into /tmp/hello.txt"
assert controller.set_answer(request, '(write-file "/tmp/hello.txt" "Hello")')

answer = agent.chat(frame_prompt(request))

assert answer.startswith('(write-file "/tmp/hello.txt" "Hello")')

def test_answer_completes_the_frame(self, agent, controller):
request = "[REQ-2] send Done"
assert controller.set_answer(request, '(send "Done")')

answer = agent.chat(frame_prompt(request))

assert answer == f'(send "Done") {FRAME_COMPLETION_ANSWER}'

def test_own_completion_is_not_duplicated(self, agent, controller):
request = "[REQ-3] send Done"
response = '(send "Done") (complete-goals-ltm "kept for later")'
assert controller.set_answer(request, response)

assert agent.chat(frame_prompt(request)) == response

def test_frame_kept_open_when_test_asks_for_it(self, agent, controller):
request = "[REQ-4] send Done"
assert controller.set_answer(request, '(send "Done")', complete_frame=False)

assert agent.chat(frame_prompt(request)) == '(send "Done")'

def test_answer_is_served_once_then_frame_is_drained(self, agent, controller):
request = "[REQ-5] send Done"
assert controller.set_answer(request, '(send "Done")')
prompt = frame_prompt(request)

first = agent.chat(prompt)
second = agent.chat(prompt)
third = agent.chat(prompt)
fourth = agent.chat(prompt)

assert first.startswith('(send "Done")')
assert second == FRAME_COMPLETION_ANSWER
assert third == FRAME_COMPLETION_ANSWER
assert fourth == ""

def test_same_request_in_a_new_frame_is_answered_again(self, agent, controller):
request = "[REQ-6] send Done"
assert controller.set_answer(request, '(send "Done")')

assert agent.chat(frame_prompt(request)).startswith('(send "Done")')
assert agent.chat(frame_prompt(request, frame=OTHER_FRAME)).startswith('(send "Done")')

def test_escaped_request_matches(self, agent, controller):
request = 'don\'t write "Hello world" into\n/tmp/e.txt'
escaped = 'don_apostrophe_t write _quote_Hello world_quote_ into_newline_/tmp/e.txt'
assert controller.set_answer(request, '(send "ok")')

assert agent.chat(frame_prompt(escaped)).startswith('(send "ok")')

def test_unknown_frame_is_not_answered(self, agent, controller):
assert controller.set_answer("[REQ-7] something else", '(send "Done")')

assert agent.chat(frame_prompt("[REQ-8] not registered")) == ""

def test_request_outside_the_frame_section_is_not_answered(self, agent, controller):
request = "[REQ-9] send Done"
assert controller.set_answer(request, '(send "Done")')
prompt = frame_prompt("another task entirely")
prompt = prompt.replace("OUTPUT_FORMAT:", f"LAST_SKILL_USE_RESULTS: {request}_newline_OUTPUT_FORMAT:")

assert agent.chat(prompt) == ""
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -128,4 +128,4 @@ RUN cp ${OMEGACLAW_DIR}/run.metta /PeTTa/run.metta \
&& chown -R 65534:65534 /opt/huggingface /opt/sentence_transformers

ENTRYPOINT ["/PeTTa/repos/OmegaClaw-Core/entrypoint.sh"]
CMD []
CMD []
3 changes: 2 additions & 1 deletion channels/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@
def get_proxy_url():
global _proxy_url
if _proxy_url is None:
_proxy_url = config_get_by_key("GATEWAY_URL", "").rstrip("/")
configured_url = config_get_by_key("GATEWAY_URL", "")
_proxy_url = str(configured_url or "").strip().rstrip("/")
return _proxy_url


Expand Down
Loading
Loading