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.
- package/.env.example +9 -0
- package/README.md +87 -4
- package/bin/lampson.js +1 -1
- package/chat.syn +133 -0
- package/cli.syn +68 -0
- package/hub.tpl.syn +127 -0
- package/lampson.ps1 +77 -51
- package/lampson.sh +68 -22
- package/lib/agents.syn +1 -1
- package/lib/approvals.syn +179 -0
- package/lib/lsp.syn +10 -1
- package/lib/mcp.syn +10 -1
- package/lib/permission.syn +18 -0
- package/lib/sched_run.syn +161 -0
- package/lib/schedule.syn +660 -0
- package/lib/session.syn +38 -1
- package/lib/settings.syn +66 -3
- package/lib/skills.syn +2 -0
- package/lib/tools.syn +94 -2
- package/lib/workspaces.syn +555 -0
- package/package.json +3 -1
- package/public/css/chat.css +56 -0
- package/public/css/hub.css +12 -0
- package/public/css/layout.css +97 -0
- package/public/css/panel.css +125 -0
- package/public/css/sidebar.css +80 -0
- package/public/css/tokens.css +57 -0
- package/public/hub.html +36 -0
- package/public/index.html +51 -1197
- package/public/js/agents.js +31 -0
- package/public/js/app.js +21 -0
- package/public/js/approvals.js +26 -0
- package/public/js/chat.js +92 -0
- package/public/js/config.js +100 -0
- package/public/js/core.js +91 -0
- package/public/js/events.js +31 -0
- package/public/js/hub.js +40 -0
- package/public/js/lamps.js +112 -0
- package/public/js/lsp.js +81 -0
- package/public/js/mcp.js +74 -0
- package/public/js/memory.js +17 -0
- package/public/js/panel.js +101 -0
- package/public/js/procs.js +45 -0
- package/public/js/schedules.js +118 -0
- package/public/js/sessions.js +69 -0
- package/public/js/sidebar.js +33 -0
- package/public/js/terminal.js +49 -0
- package/public/js/theme.js +6 -0
- package/public/js/todo.js +10 -0
- package/public/js/tree.js +53 -0
- package/public/js/update.js +14 -0
- package/public/js/workspaces.js +83 -0
- package/skills/lampson/SKILL.md +21 -0
- package/skills/synsema/SKILL.md +4 -1
- 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
|