lampson 0.1.3 → 0.2.0

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.
Files changed (55) hide show
  1. package/.env.example +9 -0
  2. package/README.md +87 -4
  3. package/bin/lampson.js +1 -1
  4. package/chat.syn +133 -0
  5. package/cli.syn +68 -0
  6. package/hub.tpl.syn +127 -0
  7. package/lampson.ps1 +77 -51
  8. package/lampson.sh +68 -22
  9. package/lib/agents.syn +1 -1
  10. package/lib/approvals.syn +179 -0
  11. package/lib/lsp.syn +10 -1
  12. package/lib/mcp.syn +10 -1
  13. package/lib/permission.syn +18 -0
  14. package/lib/sched_run.syn +161 -0
  15. package/lib/schedule.syn +660 -0
  16. package/lib/session.syn +38 -1
  17. package/lib/settings.syn +66 -3
  18. package/lib/skills.syn +2 -0
  19. package/lib/tools.syn +94 -2
  20. package/lib/workspaces.syn +555 -0
  21. package/package.json +3 -1
  22. package/public/css/chat.css +56 -0
  23. package/public/css/hub.css +12 -0
  24. package/public/css/layout.css +97 -0
  25. package/public/css/panel.css +125 -0
  26. package/public/css/sidebar.css +80 -0
  27. package/public/css/tokens.css +57 -0
  28. package/public/hub.html +36 -0
  29. package/public/index.html +51 -1197
  30. package/public/js/agents.js +31 -0
  31. package/public/js/app.js +21 -0
  32. package/public/js/approvals.js +26 -0
  33. package/public/js/chat.js +92 -0
  34. package/public/js/config.js +100 -0
  35. package/public/js/core.js +91 -0
  36. package/public/js/events.js +31 -0
  37. package/public/js/hub.js +40 -0
  38. package/public/js/lamps.js +112 -0
  39. package/public/js/lsp.js +81 -0
  40. package/public/js/mcp.js +74 -0
  41. package/public/js/memory.js +17 -0
  42. package/public/js/panel.js +101 -0
  43. package/public/js/procs.js +45 -0
  44. package/public/js/schedules.js +118 -0
  45. package/public/js/sessions.js +69 -0
  46. package/public/js/sidebar.js +33 -0
  47. package/public/js/terminal.js +49 -0
  48. package/public/js/theme.js +6 -0
  49. package/public/js/todo.js +10 -0
  50. package/public/js/tree.js +53 -0
  51. package/public/js/update.js +14 -0
  52. package/public/js/workspaces.js +83 -0
  53. package/skills/lampson/SKILL.md +21 -0
  54. package/skills/synsema/SKILL.md +4 -1
  55. package/web.syn +165 -55
@@ -0,0 +1,161 @@
1
+ -- lib/sched_run.syn — ejecutar UNA tarea programada (lamp | bash | prompt) y dejar registro
2
+ --
3
+ -- Separado de lib/schedule.syn porque las corridas `prompt` necesitan agents/loop/provider, y schedule.syn es
4
+ -- importado por tools.syn (que agents.syn importa): acá no hay ciclo. Lo usan web.syn (tick del cron y
5
+ -- POST /api/schedules/run) y chat.syn (/schedule run).
6
+ --
7
+ -- Una corrida `prompt` es un turno del agente con historial nuevo: system prompt del perfil + el texto de la
8
+ -- tarea como mensaje de usuario, sin subagentes (ni delegate ni mcp: un cron no debería multiplicarse) y con
9
+ -- el sobre de permisos de la tarea:
10
+ -- strict → lo peligroso se deniega y el agente lo informa; yolo → pasa;
11
+ -- ask → sale una aprobación por lib/approvals.syn (UI web / webhook / link) y se espera hasta
12
+ -- approval_timeout (default 2 h); sin respuesta = denegado.
13
+ -- La sesión queda guardada como cualquier otra (título «⏰ nombre», meta.schedule = id) con su traza, así se
14
+ -- revisa en la web o con /resume.
15
+
16
+ use "./schedule.syn" as schedule
17
+ use "./approvals.syn" as approvals
18
+ use "./agents.syn" as agents
19
+ use "./loop.syn" as loop
20
+ use "./provider.syn" as provider
21
+ use "./prompt.syn" as prompt
22
+ use "./session.syn" as session
23
+ use "./trace.syn" as trace
24
+ use "./permission.syn" as permission
25
+ use "./tools.syn" as tools
26
+ use "./lamps.syn" as lamps
27
+
28
+ let PROMPT_NOTE be "\n\n# Scheduled run\n- This is an UNATTENDED scheduled run (no user is watching). Do the task, verify, and finish with a short report: what you found or changed, with paths. Do not ask questions - if something is missing, say so in the report.\n- A permission request may take hours to be answered (the user gets a link). Prefer actions that need no approval; if one is denied, do not retry it - report it.\n- ONLY the workspace is accessible: absolute paths, `..`, home folders, other projects and skill folders outside it do NOT exist for your tools (a `File not found` there is final - never retry). Work with what is inside the workspace and what you already know."
29
+
30
+ -- ask_fn para corridas desatendidas en modo ask: cola + webhook + link, espera acotada. La tarea en curso
31
+ -- viaja por el blackboard (un tick corre las tareas en secuencia; el loop llama ask_fn con args nombrados).
32
+ export task ask_remote(name, args, why)
33
+ require net
34
+ require time
35
+ require random
36
+ require env("LAMPSON_*")
37
+ require file.read(".lampson")
38
+ require file.read(".lampson/*")
39
+ require file.read("workspace")
40
+ require file.read("workspace/*")
41
+ observe "lampson:sched:current" as t
42
+ let timeout be when t == nothing then 7200 otherwise t["approval_timeout"]
43
+ let label be when t == nothing then "" otherwise "[" + t["name"] + "] "
44
+ let id be approvals.open(when t == nothing then "schedule" otherwise "schedule:" + t["id"], label + permission.describe_call(name, args), why, timeout)
45
+ give approvals.wait(id, timeout)
46
+
47
+ -- on_event por defecto: solo la traza de la sesión en curso
48
+ task trace_only(kind, data, tag)
49
+ require time
50
+ require file(".lampson")
51
+ require file(".lampson/*")
52
+ observe "lampson:sched:session" as s
53
+ when s != nothing
54
+ trace.event(s, kind, data, tag)
55
+ give true
56
+
57
+ -- corre la tarea; ask_fn = nothing → aprobaciones remotas (approvals.syn); on_event = nothing → solo traza
58
+ export task run(t, ask_fn, on_event)
59
+ require net
60
+ require time
61
+ require exec
62
+ require random
63
+ require env("LAMPSON_*")
64
+ require env("OS")
65
+ require secret("LAMPSON_*")
66
+ require file("workspace")
67
+ require file("workspace/*")
68
+ require file.read("skills")
69
+ require file.read("skills/*")
70
+ require file.read("lamps")
71
+ require file.read("lamps/*")
72
+ require file("memory")
73
+ require file("memory/*")
74
+ require file(".lampson")
75
+ require file(".lampson/*")
76
+ let started be now()
77
+ schedule.mark_started(t)
78
+ when t["action"]["type"] != "prompt"
79
+ let out be ""
80
+ let status be "ok"
81
+ try
82
+ set out to schedule.run_simple(t)
83
+ recover err
84
+ set out to "ERROR: " + text(err)
85
+ when starts_with(out, "ERROR") or starts_with(out, "DENIED")
86
+ set status to "error"
87
+ give schedule.record(t, started, status, out, {})
88
+ -- prompt: un turno del agente con contexto nuevo
89
+ let sid be session.new_id()
90
+ let result be nothing
91
+ try
92
+ let cfg be provider.config()
93
+ let p be t["action"]["agent"]
94
+ let names be where(agents.profile(p)["tools"], (n) => n != "delegate" and n != "mcp" and n != "schedule")
95
+ let reg be tools.registry_subset(names)
96
+ let cat be tools.catalog_subset(names)
97
+ -- lámparas encendidas también (las de solo lectura si el perfil no edita)
98
+ let ro be not (p == "build")
99
+ each ln in lamps.names(ro)
100
+ set reg[ln] to "lamp"
101
+ set cat to cat + lamps.catalog(ro)
102
+ let asker be ask_fn
103
+ when asker == nothing and t["permission"] == "ask"
104
+ set asker to ask_remote
105
+ share t as "lampson:sched:current"
106
+ let opts be loop.default_opts(reg, cat, asker)
107
+ set opts to loop.with_steps(opts, agents.steps_for(p))
108
+ set opts to loop.with_mode(opts, t["permission"])
109
+ let system be {"role": "system", "content": prompt.build(agents.env_info(cfg), agents.addendum_for(p) + PROMPT_NOTE)}
110
+ let messages be [system, {"role": "user", "content": t["action"]["prompt"]}]
111
+ -- sesión de ESTA corrida (no pisar "lampson:session": en la terminal el chat del usuario sigue vivo al lado)
112
+ share sid as "lampson:sched:session"
113
+ schedule.set_session(t["id"], sid)
114
+ trace.user(sid, "(scheduled: " + t["name"] + ") " + t["action"]["prompt"], p, t["permission"], cfg["model"])
115
+ let emitter be when on_event == nothing then trace_only otherwise on_event
116
+ set result to loop.run_turn(cfg, messages, opts, emitter)
117
+ trace.turn_end(sid, result)
118
+ session.save(sid, result["messages"], {"title": "⏰ " + t["name"], "schedule": t["id"]})
119
+ recover err
120
+ set result to {"text": "ERROR: " + text(err), "steps": 0, "usage": {"input": 0, "output": 0}, "stopped": "error"}
121
+ let status be when result["stopped"] == "done" then "ok" otherwise ("error: " + result["stopped"])
122
+ let out be when result["text"] == "" then "(the agent produced no final text)" otherwise result["text"]
123
+ give schedule.record(t, started, status, out, {"session": sid, "steps": result["steps"], "tokens": result["usage"]["input"] + result["usage"]["output"]})
124
+
125
+ -- un tick del daemon: corre lo vencido (secuencial; el cron nunca solapa dos ticks)
126
+ export task tick()
127
+ require net
128
+ require time
129
+ require exec
130
+ require random
131
+ require env("LAMPSON_*")
132
+ require env("OS")
133
+ require secret("LAMPSON_*")
134
+ require file("workspace")
135
+ require file("workspace/*")
136
+ require file.read("skills")
137
+ require file.read("skills/*")
138
+ require file.read("lamps")
139
+ require file.read("lamps/*")
140
+ require file("memory")
141
+ require file("memory/*")
142
+ require file(".lampson")
143
+ require file(".lampson/*")
144
+ schedule.heartbeat()
145
+ -- la junction ./workspace apunta a OTRO proyecto (el usuario abrió lampson en otra carpeta): no correr nada acá
146
+ when not schedule.mount_ok()
147
+ observe "lampson:sched:mount_warned" as warned
148
+ when warned == nothing
149
+ print("[lampson] ./workspace ya no apunta a " + env("LAMPSON_WORKSPACE", "?") + ": las tareas programadas de este workspace quedan en pausa hasta reiniciar el daemon desde su carpeta (lampson --daemon restart)")
150
+ share true as "lampson:sched:mount_warned"
151
+ bus_publish("schedule.mount", {"ok": false})
152
+ give 0
153
+ share nothing as "lampson:sched:mount_warned"
154
+ let n be 0
155
+ each t in schedule.due(now())
156
+ set n to n + 1
157
+ try
158
+ run(t, nothing, nothing)
159
+ recover err
160
+ print("[lampson] scheduled task " + t["id"] + " failed: " + text(err))
161
+ give n