deepclause-sdk 0.0.11 → 0.0.13

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.
@@ -241,6 +241,7 @@ process_clause((:- Directive), SessionId) :-
241
241
  %% Handle tool/2 with description: tool(Head, Description) :- Body
242
242
  process_clause((tool(ToolHead, Description) :- Body), SessionId) :-
243
243
  !,
244
+ (catch(expand_dict_dots(Body, ExpandedBody), _, fail) -> true ; ExpandedBody = Body),
244
245
  extract_tool_schema(ToolHead, Description, ToolName, Schema),
245
246
  assertz(session_user_tools(SessionId, ToolName)),
246
247
  assertz(session_user_tool_schema(SessionId, ToolName, Schema)),
@@ -248,11 +249,12 @@ process_clause((tool(ToolHead, Description) :- Body), SessionId) :-
248
249
  format(string(SourceCode), "tool(~w, ~q) :-~n ~w.", [ToolHead, Description, Body]),
249
250
  assertz(SessionId:tool_source(ToolName, SourceCode)),
250
251
  % Assert the tool implementation (use just ToolHead for execution)
251
- assertz(SessionId:(tool(ToolHead) :- Body)).
252
+ assertz(SessionId:(tool(ToolHead) :- ExpandedBody)).
252
253
 
253
254
  %% Handle tool/1 without description: tool(Head) :- Body
254
255
  process_clause((tool(ToolHead) :- Body), SessionId) :-
255
256
  !,
257
+ (catch(expand_dict_dots(Body, ExpandedBody), _, fail) -> true ; ExpandedBody = Body),
256
258
  extract_tool_schema(ToolHead, none, ToolName, Schema),
257
259
  assertz(session_user_tools(SessionId, ToolName)),
258
260
  assertz(session_user_tool_schema(SessionId, ToolName, Schema)),
@@ -260,17 +262,86 @@ process_clause((tool(ToolHead) :- Body), SessionId) :-
260
262
  format(string(SourceCode), "tool(~w) :-~n ~w.", [ToolHead, Body]),
261
263
  assertz(SessionId:tool_source(ToolName, SourceCode)),
262
264
  % Assert the tool implementation
263
- assertz(SessionId:(tool(ToolHead) :- Body)).
265
+ assertz(SessionId:(tool(ToolHead) :- ExpandedBody)).
264
266
 
265
267
  process_clause((Head :- Body), SessionId) :-
266
268
  !,
267
- % Regular clause - assert it
268
- assertz(SessionId:(Head :- Body)).
269
+ % Expand dict dot-notation (Dict.Key → get_dict), then assert
270
+ ( catch(expand_dict_dots(Body, ExpandedBody), _, fail)
271
+ -> true
272
+ ; ExpandedBody = Body
273
+ ),
274
+ assertz(SessionId:(Head :- ExpandedBody)).
269
275
 
270
276
  process_clause(Fact, SessionId) :-
271
277
  % Simple fact
272
278
  assertz(SessionId:Fact).
273
279
 
280
+ %% ============================================================
281
+ %% Dict Dot-Notation Expansion (compile-time)
282
+ %% ============================================================
283
+ %% SWI-Prolog's dict functional notation (Dict.Key) is normally
284
+ %% expanded by goal_expansion during compilation. Since DML clauses
285
+ %% are loaded via assertz (no goal expansion), we must expand
286
+ %% Dict.Key → get_dict(Key, Dict, Value) manually before asserting.
287
+ %%
288
+ %% Handles:
289
+ %% Dict.Key = Value → get_dict(Key, Dict, Value)
290
+ %% Value = Dict.Key → get_dict(Key, Dict, Value)
291
+ %% Dict.Key == Value → get_dict(Key, Dict, Tmp), Tmp == Value
292
+ %% Value == Dict.Key → get_dict(Key, Dict, Tmp), Tmp == Value
293
+
294
+ %% expand_dict_dots(+GoalIn, -GoalOut)
295
+ %% Top-level expansion for goal bodies
296
+ expand_dict_dots(Var, Var) :- var(Var), !.
297
+ expand_dict_dots((A, B), (EA, EB)) :- !, expand_dict_dots(A, EA), expand_dict_dots(B, EB).
298
+ expand_dict_dots((A ; B), (EA ; EB)) :- !, expand_dict_dots(A, EA), expand_dict_dots(B, EB).
299
+ expand_dict_dots((A -> B), (EA -> EB)) :- !, expand_dict_dots(A, EA), expand_dict_dots(B, EB).
300
+
301
+ %% Unification with dict dot access: Dict.Key = Value
302
+ expand_dict_dots(Goal, Result) :-
303
+ nonvar(Goal),
304
+ functor(Goal, =, 2),
305
+ !,
306
+ arg(1, Goal, A),
307
+ arg(2, Goal, B),
308
+ ( nonvar(A), is_dot_access(A, Dict, Key)
309
+ -> Result = get_dict(Key, Dict, B)
310
+ ; nonvar(B), is_dot_access(B, Dict, Key)
311
+ -> Result = get_dict(Key, Dict, A)
312
+ ; Result = Goal
313
+ ).
314
+
315
+ %% Equality check with dict dot access: Dict.Key == Value
316
+ expand_dict_dots(Goal, Result) :-
317
+ nonvar(Goal),
318
+ functor(Goal, ==, 2),
319
+ !,
320
+ arg(1, Goal, A),
321
+ arg(2, Goal, B),
322
+ ( nonvar(A), is_dot_access(A, Dict, Key)
323
+ -> Result = (get_dict(Key, Dict, Tmp), Tmp == B)
324
+ ; nonvar(B), is_dot_access(B, Dict, Key)
325
+ -> Result = (get_dict(Key, Dict, Tmp), Tmp == A)
326
+ ; Result = Goal
327
+ ).
328
+
329
+ expand_dict_dots(Goal, Goal).
330
+
331
+ %% is_dot_access(+Term, -Dict, -Key)
332
+ %% Check if Term is a dict dot-access expression: '.'(Dict, Key) where Key is atom
333
+ is_dot_access(Term, Dict, Key) :-
334
+ compound(Term),
335
+ compound_name_arity(Term, '.', 2),
336
+ arg(1, Term, Dict),
337
+ arg(2, Term, Key),
338
+ atom(Key).
339
+
340
+ %% expand_dict_expr(+ExprIn, -ExprOut)
341
+ %% Expand dict access in value-level expressions (just pass through for now)
342
+ expand_dict_expr(Var, Var) :- var(Var), !.
343
+ expand_dict_expr(Expr, Expr).
344
+
274
345
  %% ============================================================
275
346
  %% Tool Schema Extraction
276
347
  %% ============================================================
@@ -560,6 +631,12 @@ set_context_stack(StateIn, Stack, StateOut) :-
560
631
  set_memory(StateIn, Memory, StateOut) :-
561
632
  StateOut = StateIn.put(memory, Memory).
562
633
 
634
+ %% is_system_memory_message(+Message)
635
+ %% True if the memory message has role=system
636
+ is_system_memory_message(Message) :-
637
+ is_dict(Message),
638
+ get_dict(role, Message, system).
639
+
563
640
  %% ============================================================
564
641
  %% Tool Scope Helpers
565
642
  %% ============================================================
@@ -928,8 +1005,11 @@ mi_call(task(Desc), StateIn, StateOut) :-
928
1005
  ),
929
1006
  % Use full messages from agent loop result
930
1007
  ( get_dict(messages, Result, Messages), Messages \= []
931
- -> % Replace memory with all messages from agent (includes system, history, and new messages)
932
- set_memory(StateAfterAgent, Messages, StateOut)
1008
+ -> % Preserve system messages from current memory (agent only returns user/assistant)
1009
+ get_memory(StateAfterAgent, OldMemory),
1010
+ include(is_system_memory_message, OldMemory, SystemMessages),
1011
+ append(SystemMessages, Messages, NewMemory),
1012
+ set_memory(StateAfterAgent, NewMemory, StateOut)
933
1013
  ; % Fallback: just add task description and response (old behavior)
934
1014
  add_memory(StateAfterAgent, user, InterpDesc, State1),
935
1015
  ( get_dict(response, Result, Response), Response \= ""
@@ -1040,8 +1120,11 @@ mi_call_task_n(Desc, Vars, VarNames, StateIn, StateOut) :-
1040
1120
  bind_task_variables(Result.variables, VarNames, Vars),
1041
1121
  % Use full messages from agent loop result
1042
1122
  ( get_dict(messages, Result, Messages), Messages \= []
1043
- -> % Replace memory with all messages from agent
1044
- set_memory(StateAfterAgent, Messages, StateOut)
1123
+ -> % Preserve system messages from current memory (agent only returns user/assistant)
1124
+ get_memory(StateAfterAgent, OldMemory),
1125
+ include(is_system_memory_message, OldMemory, SystemMessages),
1126
+ append(SystemMessages, Messages, NewMemory),
1127
+ set_memory(StateAfterAgent, NewMemory, StateOut)
1045
1128
  ; % Fallback: just add task description and response (old behavior)
1046
1129
  add_memory(StateAfterAgent, user, InterpDesc, State1),
1047
1130
  ( get_dict(response, Result, Response), Response \= ""
@@ -1585,9 +1668,10 @@ consult_process_term((tool(ToolHead) :- Body), SessionId) :-
1585
1668
  assertz(SessionId:(tool(ToolHead) :- Body)).
1586
1669
 
1587
1670
  consult_process_term((Head :- Body), SessionId) :-
1588
- % Assert a clause
1671
+ % Expand dict dot-notation, then assert
1589
1672
  !,
1590
- assertz(SessionId:(Head :- Body)).
1673
+ (catch(expand_dict_dots(Body, ExpandedBody), _, fail) -> true ; ExpandedBody = Body),
1674
+ assertz(SessionId:(Head :- ExpandedBody)).
1591
1675
 
1592
1676
  consult_process_term(Head, SessionId) :-
1593
1677
  % Assert a fact
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deepclause-sdk",
3
- "version": "0.0.11",
3
+ "version": "0.0.13",
4
4
  "description": "DeepClause CLI Tool and Typescript SDK",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -241,6 +241,7 @@ process_clause((:- Directive), SessionId) :-
241
241
  %% Handle tool/2 with description: tool(Head, Description) :- Body
242
242
  process_clause((tool(ToolHead, Description) :- Body), SessionId) :-
243
243
  !,
244
+ (catch(expand_dict_dots(Body, ExpandedBody), _, fail) -> true ; ExpandedBody = Body),
244
245
  extract_tool_schema(ToolHead, Description, ToolName, Schema),
245
246
  assertz(session_user_tools(SessionId, ToolName)),
246
247
  assertz(session_user_tool_schema(SessionId, ToolName, Schema)),
@@ -248,11 +249,12 @@ process_clause((tool(ToolHead, Description) :- Body), SessionId) :-
248
249
  format(string(SourceCode), "tool(~w, ~q) :-~n ~w.", [ToolHead, Description, Body]),
249
250
  assertz(SessionId:tool_source(ToolName, SourceCode)),
250
251
  % Assert the tool implementation (use just ToolHead for execution)
251
- assertz(SessionId:(tool(ToolHead) :- Body)).
252
+ assertz(SessionId:(tool(ToolHead) :- ExpandedBody)).
252
253
 
253
254
  %% Handle tool/1 without description: tool(Head) :- Body
254
255
  process_clause((tool(ToolHead) :- Body), SessionId) :-
255
256
  !,
257
+ (catch(expand_dict_dots(Body, ExpandedBody), _, fail) -> true ; ExpandedBody = Body),
256
258
  extract_tool_schema(ToolHead, none, ToolName, Schema),
257
259
  assertz(session_user_tools(SessionId, ToolName)),
258
260
  assertz(session_user_tool_schema(SessionId, ToolName, Schema)),
@@ -260,17 +262,86 @@ process_clause((tool(ToolHead) :- Body), SessionId) :-
260
262
  format(string(SourceCode), "tool(~w) :-~n ~w.", [ToolHead, Body]),
261
263
  assertz(SessionId:tool_source(ToolName, SourceCode)),
262
264
  % Assert the tool implementation
263
- assertz(SessionId:(tool(ToolHead) :- Body)).
265
+ assertz(SessionId:(tool(ToolHead) :- ExpandedBody)).
264
266
 
265
267
  process_clause((Head :- Body), SessionId) :-
266
268
  !,
267
- % Regular clause - assert it
268
- assertz(SessionId:(Head :- Body)).
269
+ % Expand dict dot-notation (Dict.Key → get_dict), then assert
270
+ ( catch(expand_dict_dots(Body, ExpandedBody), _, fail)
271
+ -> true
272
+ ; ExpandedBody = Body
273
+ ),
274
+ assertz(SessionId:(Head :- ExpandedBody)).
269
275
 
270
276
  process_clause(Fact, SessionId) :-
271
277
  % Simple fact
272
278
  assertz(SessionId:Fact).
273
279
 
280
+ %% ============================================================
281
+ %% Dict Dot-Notation Expansion (compile-time)
282
+ %% ============================================================
283
+ %% SWI-Prolog's dict functional notation (Dict.Key) is normally
284
+ %% expanded by goal_expansion during compilation. Since DML clauses
285
+ %% are loaded via assertz (no goal expansion), we must expand
286
+ %% Dict.Key → get_dict(Key, Dict, Value) manually before asserting.
287
+ %%
288
+ %% Handles:
289
+ %% Dict.Key = Value → get_dict(Key, Dict, Value)
290
+ %% Value = Dict.Key → get_dict(Key, Dict, Value)
291
+ %% Dict.Key == Value → get_dict(Key, Dict, Tmp), Tmp == Value
292
+ %% Value == Dict.Key → get_dict(Key, Dict, Tmp), Tmp == Value
293
+
294
+ %% expand_dict_dots(+GoalIn, -GoalOut)
295
+ %% Top-level expansion for goal bodies
296
+ expand_dict_dots(Var, Var) :- var(Var), !.
297
+ expand_dict_dots((A, B), (EA, EB)) :- !, expand_dict_dots(A, EA), expand_dict_dots(B, EB).
298
+ expand_dict_dots((A ; B), (EA ; EB)) :- !, expand_dict_dots(A, EA), expand_dict_dots(B, EB).
299
+ expand_dict_dots((A -> B), (EA -> EB)) :- !, expand_dict_dots(A, EA), expand_dict_dots(B, EB).
300
+
301
+ %% Unification with dict dot access: Dict.Key = Value
302
+ expand_dict_dots(Goal, Result) :-
303
+ nonvar(Goal),
304
+ functor(Goal, =, 2),
305
+ !,
306
+ arg(1, Goal, A),
307
+ arg(2, Goal, B),
308
+ ( nonvar(A), is_dot_access(A, Dict, Key)
309
+ -> Result = get_dict(Key, Dict, B)
310
+ ; nonvar(B), is_dot_access(B, Dict, Key)
311
+ -> Result = get_dict(Key, Dict, A)
312
+ ; Result = Goal
313
+ ).
314
+
315
+ %% Equality check with dict dot access: Dict.Key == Value
316
+ expand_dict_dots(Goal, Result) :-
317
+ nonvar(Goal),
318
+ functor(Goal, ==, 2),
319
+ !,
320
+ arg(1, Goal, A),
321
+ arg(2, Goal, B),
322
+ ( nonvar(A), is_dot_access(A, Dict, Key)
323
+ -> Result = (get_dict(Key, Dict, Tmp), Tmp == B)
324
+ ; nonvar(B), is_dot_access(B, Dict, Key)
325
+ -> Result = (get_dict(Key, Dict, Tmp), Tmp == A)
326
+ ; Result = Goal
327
+ ).
328
+
329
+ expand_dict_dots(Goal, Goal).
330
+
331
+ %% is_dot_access(+Term, -Dict, -Key)
332
+ %% Check if Term is a dict dot-access expression: '.'(Dict, Key) where Key is atom
333
+ is_dot_access(Term, Dict, Key) :-
334
+ compound(Term),
335
+ compound_name_arity(Term, '.', 2),
336
+ arg(1, Term, Dict),
337
+ arg(2, Term, Key),
338
+ atom(Key).
339
+
340
+ %% expand_dict_expr(+ExprIn, -ExprOut)
341
+ %% Expand dict access in value-level expressions (just pass through for now)
342
+ expand_dict_expr(Var, Var) :- var(Var), !.
343
+ expand_dict_expr(Expr, Expr).
344
+
274
345
  %% ============================================================
275
346
  %% Tool Schema Extraction
276
347
  %% ============================================================
@@ -560,6 +631,12 @@ set_context_stack(StateIn, Stack, StateOut) :-
560
631
  set_memory(StateIn, Memory, StateOut) :-
561
632
  StateOut = StateIn.put(memory, Memory).
562
633
 
634
+ %% is_system_memory_message(+Message)
635
+ %% True if the memory message has role=system
636
+ is_system_memory_message(Message) :-
637
+ is_dict(Message),
638
+ get_dict(role, Message, system).
639
+
563
640
  %% ============================================================
564
641
  %% Tool Scope Helpers
565
642
  %% ============================================================
@@ -928,8 +1005,11 @@ mi_call(task(Desc), StateIn, StateOut) :-
928
1005
  ),
929
1006
  % Use full messages from agent loop result
930
1007
  ( get_dict(messages, Result, Messages), Messages \= []
931
- -> % Replace memory with all messages from agent (includes system, history, and new messages)
932
- set_memory(StateAfterAgent, Messages, StateOut)
1008
+ -> % Preserve system messages from current memory (agent only returns user/assistant)
1009
+ get_memory(StateAfterAgent, OldMemory),
1010
+ include(is_system_memory_message, OldMemory, SystemMessages),
1011
+ append(SystemMessages, Messages, NewMemory),
1012
+ set_memory(StateAfterAgent, NewMemory, StateOut)
933
1013
  ; % Fallback: just add task description and response (old behavior)
934
1014
  add_memory(StateAfterAgent, user, InterpDesc, State1),
935
1015
  ( get_dict(response, Result, Response), Response \= ""
@@ -1040,8 +1120,11 @@ mi_call_task_n(Desc, Vars, VarNames, StateIn, StateOut) :-
1040
1120
  bind_task_variables(Result.variables, VarNames, Vars),
1041
1121
  % Use full messages from agent loop result
1042
1122
  ( get_dict(messages, Result, Messages), Messages \= []
1043
- -> % Replace memory with all messages from agent
1044
- set_memory(StateAfterAgent, Messages, StateOut)
1123
+ -> % Preserve system messages from current memory (agent only returns user/assistant)
1124
+ get_memory(StateAfterAgent, OldMemory),
1125
+ include(is_system_memory_message, OldMemory, SystemMessages),
1126
+ append(SystemMessages, Messages, NewMemory),
1127
+ set_memory(StateAfterAgent, NewMemory, StateOut)
1045
1128
  ; % Fallback: just add task description and response (old behavior)
1046
1129
  add_memory(StateAfterAgent, user, InterpDesc, State1),
1047
1130
  ( get_dict(response, Result, Response), Response \= ""
@@ -1585,9 +1668,10 @@ consult_process_term((tool(ToolHead) :- Body), SessionId) :-
1585
1668
  assertz(SessionId:(tool(ToolHead) :- Body)).
1586
1669
 
1587
1670
  consult_process_term((Head :- Body), SessionId) :-
1588
- % Assert a clause
1671
+ % Expand dict dot-notation, then assert
1589
1672
  !,
1590
- assertz(SessionId:(Head :- Body)).
1673
+ (catch(expand_dict_dots(Body, ExpandedBody), _, fail) -> true ; ExpandedBody = Body),
1674
+ assertz(SessionId:(Head :- ExpandedBody)).
1591
1675
 
1592
1676
  consult_process_term(Head, SessionId) :-
1593
1677
  % Assert a fact