mirror of
https://github.com/PR0M3TH3AN/Archivestr.git
synced 2026-10-27 08:30:50 +00:00
update
This commit is contained in:
1 parent
cc1ba691cb
commit
deadeaafac
73 files changed
+3085
-699
No files matched your search
@@ -0,0 +1 @@
|
|||||||
|
torch/node_modules
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
MEMORY_STORED
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
"importance": 0.4,
|
"importance": 0.4,
|
||||||
"embedding_id": null,
|
"embedding_id": null,
|
||||||
"created_at": 1771558399336,
|
"created_at": 1771558399336,
|
||||||
"last_seen": 1771558399343,
|
"last_seen": 1771596755883,
|
||||||
"source": "scheduler-retrieve",
|
"source": "scheduler-retrieve",
|
||||||
"ttl_days": null,
|
"ttl_days": null,
|
||||||
"merged_into": null,
|
"merged_into": null,
|
||||||
@@ -48,5 +48,380 @@
|
|||||||
"merged_into": null,
|
"merged_into": null,
|
||||||
"pinned": false
|
"pinned": false
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"7b35c3e0-e01a-4015-b27e-dfbe8c43ef7a",
|
||||||
|
{
|
||||||
|
"schema_version": 1,
|
||||||
|
"id": "7b35c3e0-e01a-4015-b27e-dfbe8c43ef7a",
|
||||||
|
"agent_id": "scheduler-memory-daily",
|
||||||
|
"session_id": "session-mluyytgn",
|
||||||
|
"type": "batch_window",
|
||||||
|
"content": "Memory retrieval seed for daily :: scheduler memory retrieval",
|
||||||
|
"summary": "Memory retrieval seed for daily :: scheduler memory retrieval",
|
||||||
|
"tags": [
|
||||||
|
"scheduler",
|
||||||
|
"daily",
|
||||||
|
"retrieve"
|
||||||
|
],
|
||||||
|
"importance": 0.4,
|
||||||
|
"embedding_id": null,
|
||||||
|
"created_at": 1771596755879,
|
||||||
|
"last_seen": 1771596755883,
|
||||||
|
"source": "scheduler-retrieve",
|
||||||
|
"ttl_days": null,
|
||||||
|
"merged_into": null,
|
||||||
|
"pinned": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"fbc6aad6-ae89-4bf2-8c67-0053cb5c67d9",
|
||||||
|
{
|
||||||
|
"schema_version": 1,
|
||||||
|
"id": "fbc6aad6-ae89-4bf2-8c67-0053cb5c67d9",
|
||||||
|
"agent_id": "scheduler-memory-weekly",
|
||||||
|
"session_id": "session-mluyztmu",
|
||||||
|
"type": "batch_window",
|
||||||
|
"content": "Store memory event A for weekly :: scheduler memory store\nStore memory event B for weekly :: scheduler memory store",
|
||||||
|
"summary": "Store memory event A for weekly :: scheduler memory store Store memory event B for weekly :: scheduler memory store",
|
||||||
|
"tags": [
|
||||||
|
"scheduler",
|
||||||
|
"weekly",
|
||||||
|
"store"
|
||||||
|
],
|
||||||
|
"importance": 0.55,
|
||||||
|
"embedding_id": null,
|
||||||
|
"created_at": 1771596802758,
|
||||||
|
"last_seen": 1771596802759,
|
||||||
|
"source": "scheduler-store",
|
||||||
|
"ttl_days": null,
|
||||||
|
"merged_into": null,
|
||||||
|
"pinned": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"05791126-ee9b-4c8e-913d-30a6c930b5bf",
|
||||||
|
{
|
||||||
|
"schema_version": 1,
|
||||||
|
"id": "05791126-ee9b-4c8e-913d-30a6c930b5bf",
|
||||||
|
"agent_id": "style-agent",
|
||||||
|
"session_id": "session-mluz1yd4",
|
||||||
|
"type": "batch_window",
|
||||||
|
"content": "Store memory event A for daily :: scheduler memory store\nStore memory event B for daily :: scheduler memory store",
|
||||||
|
"summary": "Store memory event A for daily :: scheduler memory store Store memory event B for daily :: scheduler memory store",
|
||||||
|
"tags": [
|
||||||
|
"scheduler",
|
||||||
|
"daily",
|
||||||
|
"store"
|
||||||
|
],
|
||||||
|
"importance": 0.55,
|
||||||
|
"embedding_id": null,
|
||||||
|
"created_at": 1771596902200,
|
||||||
|
"last_seen": 1771596902201,
|
||||||
|
"source": "scheduler-store",
|
||||||
|
"ttl_days": null,
|
||||||
|
"merged_into": null,
|
||||||
|
"pinned": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"b8705dfd-20f0-42a2-af23-de692718013d",
|
||||||
|
{
|
||||||
|
"schema_version": 1,
|
||||||
|
"id": "b8705dfd-20f0-42a2-af23-de692718013d",
|
||||||
|
"agent_id": "test-audit-agent",
|
||||||
|
"session_id": "session-mluzff2k",
|
||||||
|
"type": "batch_window",
|
||||||
|
"content": "Memory retrieval seed for daily :: scheduler memory retrieval",
|
||||||
|
"summary": "Memory retrieval seed for daily :: scheduler memory retrieval",
|
||||||
|
"tags": [
|
||||||
|
"scheduler",
|
||||||
|
"daily",
|
||||||
|
"retrieve"
|
||||||
|
],
|
||||||
|
"importance": 0.4,
|
||||||
|
"embedding_id": null,
|
||||||
|
"created_at": 1771597530380,
|
||||||
|
"last_seen": 1771597530385,
|
||||||
|
"source": "scheduler-retrieve",
|
||||||
|
"ttl_days": null,
|
||||||
|
"merged_into": null,
|
||||||
|
"pinned": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"12378812-8347-4458-a373-12c3abe905e4",
|
||||||
|
{
|
||||||
|
"schema_version": 1,
|
||||||
|
"id": "12378812-8347-4458-a373-12c3abe905e4",
|
||||||
|
"agent_id": "test-audit-agent",
|
||||||
|
"session_id": "session-mluzfucz",
|
||||||
|
"type": "batch_window",
|
||||||
|
"content": "Store memory event A for daily :: scheduler memory store\nStore memory event B for daily :: scheduler memory store",
|
||||||
|
"summary": "Store memory event A for daily :: scheduler memory store Store memory event B for daily :: scheduler memory store",
|
||||||
|
"tags": [
|
||||||
|
"scheduler",
|
||||||
|
"daily",
|
||||||
|
"store"
|
||||||
|
],
|
||||||
|
"importance": 0.55,
|
||||||
|
"embedding_id": null,
|
||||||
|
"created_at": 1771597550195,
|
||||||
|
"last_seen": 1771597550196,
|
||||||
|
"source": "scheduler-store",
|
||||||
|
"ttl_days": null,
|
||||||
|
"merged_into": null,
|
||||||
|
"pinned": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"c1b3098d-fc75-4e2f-a087-718806d32c7d",
|
||||||
|
{
|
||||||
|
"schema_version": 1,
|
||||||
|
"id": "c1b3098d-fc75-4e2f-a087-718806d32c7d",
|
||||||
|
"agent_id": "todo-triage-agent",
|
||||||
|
"session_id": "session-mluzkanb",
|
||||||
|
"type": "batch_window",
|
||||||
|
"content": "Memory retrieval seed for daily :: scheduler memory retrieval",
|
||||||
|
"summary": "Memory retrieval seed for daily :: scheduler memory retrieval",
|
||||||
|
"tags": [
|
||||||
|
"scheduler",
|
||||||
|
"daily",
|
||||||
|
"retrieve"
|
||||||
|
],
|
||||||
|
"importance": 0.4,
|
||||||
|
"embedding_id": null,
|
||||||
|
"created_at": 1771597757927,
|
||||||
|
"last_seen": 1771597757937,
|
||||||
|
"source": "scheduler-retrieve",
|
||||||
|
"ttl_days": null,
|
||||||
|
"merged_into": null,
|
||||||
|
"pinned": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"2c83e058-51fc-42fe-a68c-06b08a9a8e29",
|
||||||
|
{
|
||||||
|
"schema_version": 1,
|
||||||
|
"id": "2c83e058-51fc-42fe-a68c-06b08a9a8e29",
|
||||||
|
"agent_id": "todo-triage-agent",
|
||||||
|
"session_id": "session-mluzkknh",
|
||||||
|
"type": "batch_window",
|
||||||
|
"content": "Store memory event A for daily :: scheduler memory store\nStore memory event B for daily :: scheduler memory store",
|
||||||
|
"summary": "Store memory event A for daily :: scheduler memory store Store memory event B for daily :: scheduler memory store",
|
||||||
|
"tags": [
|
||||||
|
"scheduler",
|
||||||
|
"daily",
|
||||||
|
"store"
|
||||||
|
],
|
||||||
|
"importance": 0.55,
|
||||||
|
"embedding_id": null,
|
||||||
|
"created_at": 1771597770893,
|
||||||
|
"last_seen": 1771597770894,
|
||||||
|
"source": "scheduler-store",
|
||||||
|
"ttl_days": null,
|
||||||
|
"merged_into": null,
|
||||||
|
"pinned": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"69ba2c33-e762-4d4b-9143-3b9bcb45412f",
|
||||||
|
{
|
||||||
|
"schema_version": 1,
|
||||||
|
"id": "69ba2c33-e762-4d4b-9143-3b9bcb45412f",
|
||||||
|
"agent_id": "torch-garbage-collection-agent",
|
||||||
|
"session_id": "session-mluzsu17",
|
||||||
|
"type": "batch_window",
|
||||||
|
"content": "Memory retrieval seed for daily :: scheduler memory retrieval",
|
||||||
|
"summary": "Memory retrieval seed for daily :: scheduler memory retrieval",
|
||||||
|
"tags": [
|
||||||
|
"scheduler",
|
||||||
|
"daily",
|
||||||
|
"retrieve"
|
||||||
|
],
|
||||||
|
"importance": 0.4,
|
||||||
|
"embedding_id": null,
|
||||||
|
"created_at": 1771598156299,
|
||||||
|
"last_seen": 1771598156305,
|
||||||
|
"source": "scheduler-retrieve",
|
||||||
|
"ttl_days": null,
|
||||||
|
"merged_into": null,
|
||||||
|
"pinned": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"3f6fef8d-b01f-45d4-925c-ad692059bd35",
|
||||||
|
{
|
||||||
|
"schema_version": 1,
|
||||||
|
"id": "3f6fef8d-b01f-45d4-925c-ad692059bd35",
|
||||||
|
"agent_id": "torch-garbage-collection-agent",
|
||||||
|
"session_id": "session-mluzt2t2",
|
||||||
|
"type": "batch_window",
|
||||||
|
"content": "Store memory event A for daily :: scheduler memory store\nStore memory event B for daily :: scheduler memory store",
|
||||||
|
"summary": "Store memory event A for daily :: scheduler memory store Store memory event B for daily :: scheduler memory store",
|
||||||
|
"tags": [
|
||||||
|
"scheduler",
|
||||||
|
"daily",
|
||||||
|
"store"
|
||||||
|
],
|
||||||
|
"importance": 0.55,
|
||||||
|
"embedding_id": null,
|
||||||
|
"created_at": 1771598167670,
|
||||||
|
"last_seen": 1771598167671,
|
||||||
|
"source": "scheduler-store",
|
||||||
|
"ttl_days": null,
|
||||||
|
"merged_into": null,
|
||||||
|
"pinned": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"a047de7f-bbeb-40d0-ac23-f5a43753f664",
|
||||||
|
{
|
||||||
|
"schema_version": 1,
|
||||||
|
"id": "a047de7f-bbeb-40d0-ac23-f5a43753f664",
|
||||||
|
"agent_id": "audit-agent",
|
||||||
|
"session_id": "session-mlv03xay",
|
||||||
|
"type": "batch_window",
|
||||||
|
"content": "Memory retrieval seed for daily :: scheduler memory retrieval",
|
||||||
|
"summary": "Memory retrieval seed for daily :: scheduler memory retrieval",
|
||||||
|
"tags": [
|
||||||
|
"scheduler",
|
||||||
|
"daily",
|
||||||
|
"retrieve"
|
||||||
|
],
|
||||||
|
"importance": 0.4,
|
||||||
|
"embedding_id": null,
|
||||||
|
"created_at": 1771598673754,
|
||||||
|
"last_seen": 1771598673758,
|
||||||
|
"source": "scheduler-retrieve",
|
||||||
|
"ttl_days": null,
|
||||||
|
"merged_into": null,
|
||||||
|
"pinned": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"02b1409a-fd8f-478e-816c-bbd985e106d8",
|
||||||
|
{
|
||||||
|
"schema_version": 1,
|
||||||
|
"id": "02b1409a-fd8f-478e-816c-bbd985e106d8",
|
||||||
|
"agent_id": "audit-agent",
|
||||||
|
"session_id": "session-mlv0498w",
|
||||||
|
"type": "batch_window",
|
||||||
|
"content": "Store memory event A for daily :: scheduler memory store\nStore memory event B for daily :: scheduler memory store",
|
||||||
|
"summary": "Store memory event A for daily :: scheduler memory store Store memory event B for daily :: scheduler memory store",
|
||||||
|
"tags": [
|
||||||
|
"scheduler",
|
||||||
|
"daily",
|
||||||
|
"store"
|
||||||
|
],
|
||||||
|
"importance": 0.55,
|
||||||
|
"embedding_id": null,
|
||||||
|
"created_at": 1771598689232,
|
||||||
|
"last_seen": 1771598689233,
|
||||||
|
"source": "scheduler-store",
|
||||||
|
"ttl_days": null,
|
||||||
|
"merged_into": null,
|
||||||
|
"pinned": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"0da3cadf-a44a-4055-8e3c-838b924a2f48",
|
||||||
|
{
|
||||||
|
"schema_version": 1,
|
||||||
|
"id": "0da3cadf-a44a-4055-8e3c-838b924a2f48",
|
||||||
|
"agent_id": "ci-health-agent",
|
||||||
|
"session_id": "session-mlv16ngi",
|
||||||
|
"type": "batch_window",
|
||||||
|
"content": "Memory retrieval seed for daily :: scheduler memory retrieval",
|
||||||
|
"summary": "Memory retrieval seed for daily :: scheduler memory retrieval",
|
||||||
|
"tags": [
|
||||||
|
"scheduler",
|
||||||
|
"daily",
|
||||||
|
"retrieve"
|
||||||
|
],
|
||||||
|
"importance": 0.4,
|
||||||
|
"embedding_id": null,
|
||||||
|
"created_at": 1771600480578,
|
||||||
|
"last_seen": 1771600480583,
|
||||||
|
"source": "scheduler-retrieve",
|
||||||
|
"ttl_days": null,
|
||||||
|
"merged_into": null,
|
||||||
|
"pinned": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"24c22a90-aa05-4b71-b525-2274313658b5",
|
||||||
|
{
|
||||||
|
"schema_version": 1,
|
||||||
|
"id": "24c22a90-aa05-4b71-b525-2274313658b5",
|
||||||
|
"agent_id": "ci-health-agent",
|
||||||
|
"session_id": "session-mlv16wbs",
|
||||||
|
"type": "batch_window",
|
||||||
|
"content": "Store memory event A for daily :: scheduler memory store\nStore memory event B for daily :: scheduler memory store",
|
||||||
|
"summary": "Store memory event A for daily :: scheduler memory store Store memory event B for daily :: scheduler memory store",
|
||||||
|
"tags": [
|
||||||
|
"scheduler",
|
||||||
|
"daily",
|
||||||
|
"store"
|
||||||
|
],
|
||||||
|
"importance": 0.55,
|
||||||
|
"embedding_id": null,
|
||||||
|
"created_at": 1771600492072,
|
||||||
|
"last_seen": 1771600492073,
|
||||||
|
"source": "scheduler-store",
|
||||||
|
"ttl_days": null,
|
||||||
|
"merged_into": null,
|
||||||
|
"pinned": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"e4405f5d-ff1f-481b-9981-b5f78d344860",
|
||||||
|
{
|
||||||
|
"schema_version": 1,
|
||||||
|
"id": "e4405f5d-ff1f-481b-9981-b5f78d344860",
|
||||||
|
"agent_id": "const-refactor-agent",
|
||||||
|
"session_id": "session-mlv1h4b5",
|
||||||
|
"type": "batch_window",
|
||||||
|
"content": "Memory retrieval seed for daily :: scheduler memory retrieval",
|
||||||
|
"summary": "Memory retrieval seed for daily :: scheduler memory retrieval",
|
||||||
|
"tags": [
|
||||||
|
"scheduler",
|
||||||
|
"daily",
|
||||||
|
"retrieve"
|
||||||
|
],
|
||||||
|
"importance": 0.4,
|
||||||
|
"embedding_id": null,
|
||||||
|
"created_at": 1771600968977,
|
||||||
|
"last_seen": 1771600968992,
|
||||||
|
"source": "scheduler-retrieve",
|
||||||
|
"ttl_days": null,
|
||||||
|
"merged_into": null,
|
||||||
|
"pinned": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"fcbf743f-3818-4502-99ec-273e691f0840",
|
||||||
|
{
|
||||||
|
"schema_version": 1,
|
||||||
|
"id": "fcbf743f-3818-4502-99ec-273e691f0840",
|
||||||
|
"agent_id": "const-refactor-agent",
|
||||||
|
"session_id": "session-mlv1hbp7",
|
||||||
|
"type": "batch_window",
|
||||||
|
"content": "Store memory event A for daily :: scheduler memory store\nStore memory event B for daily :: scheduler memory store",
|
||||||
|
"summary": "Store memory event A for daily :: scheduler memory store Store memory event B for daily :: scheduler memory store",
|
||||||
|
"tags": [
|
||||||
|
"scheduler",
|
||||||
|
"daily",
|
||||||
|
"store"
|
||||||
|
],
|
||||||
|
"importance": 0.55,
|
||||||
|
"embedding_id": null,
|
||||||
|
"created_at": 1771600978555,
|
||||||
|
"last_seen": 1771600978556,
|
||||||
|
"source": "scheduler-store",
|
||||||
|
"ttl_days": null,
|
||||||
|
"merged_into": null,
|
||||||
|
"pinned": false
|
||||||
|
}
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"cadence": "daily",
|
||||||
|
"operation": "retrieve",
|
||||||
|
"runId": "session-mluyytgn",
|
||||||
|
"servicePath": "src/services/memory/index.js#getRelevantMemories",
|
||||||
|
"inputs": {
|
||||||
|
"agentId": "scheduler-memory-daily",
|
||||||
|
"promptPath": "",
|
||||||
|
"promptIntent": "scheduler memory retrieval",
|
||||||
|
"query": "scheduler | daily | agent | scheduler-memory-daily | intent | scheduler memory retrieval",
|
||||||
|
"events": 1
|
||||||
|
},
|
||||||
|
"outputs": {
|
||||||
|
"ingestedCount": 1,
|
||||||
|
"retrievedCount": 2
|
||||||
|
},
|
||||||
|
"status": "ok"
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
MEMORY_RETRIEVED
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"cadence": "weekly",
|
||||||
|
"operation": "store",
|
||||||
|
"runId": "session-mluyztmu",
|
||||||
|
"servicePath": "src/services/memory/index.js#ingestEvents",
|
||||||
|
"inputs": {
|
||||||
|
"agentId": "scheduler-memory-weekly",
|
||||||
|
"promptPath": "",
|
||||||
|
"promptIntent": "scheduler memory store",
|
||||||
|
"events": 2,
|
||||||
|
"sourceFile": "fallback"
|
||||||
|
},
|
||||||
|
"outputs": {
|
||||||
|
"storedCount": 1,
|
||||||
|
"summaries": [
|
||||||
|
"Store memory event A for weekly :: scheduler memory store Store memory event B for weekly :: scheduler memory store"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"status": "ok"
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
MEMORY_STORED
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"cadence": "daily",
|
||||||
|
"operation": "store",
|
||||||
|
"runId": "session-mluz1yd4",
|
||||||
|
"servicePath": "src/services/memory/index.js#ingestEvents",
|
||||||
|
"inputs": {
|
||||||
|
"agentId": "style-agent",
|
||||||
|
"promptPath": "",
|
||||||
|
"promptIntent": "scheduler memory store",
|
||||||
|
"events": 2,
|
||||||
|
"sourceFile": "fallback"
|
||||||
|
},
|
||||||
|
"outputs": {
|
||||||
|
"storedCount": 1,
|
||||||
|
"summaries": [
|
||||||
|
"Store memory event A for daily :: scheduler memory store Store memory event B for daily :: scheduler memory store"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"status": "ok"
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
MEMORY_STORED
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"cadence": "daily",
|
||||||
|
"operation": "retrieve",
|
||||||
|
"runId": "session-mluzff2k",
|
||||||
|
"servicePath": "src/services/memory/index.js#getRelevantMemories",
|
||||||
|
"inputs": {
|
||||||
|
"agentId": "test-audit-agent",
|
||||||
|
"promptPath": "",
|
||||||
|
"promptIntent": "scheduler memory retrieval",
|
||||||
|
"query": "scheduler | daily | agent | test-audit-agent | intent | scheduler memory retrieval",
|
||||||
|
"events": 1
|
||||||
|
},
|
||||||
|
"outputs": {
|
||||||
|
"ingestedCount": 1,
|
||||||
|
"retrievedCount": 1
|
||||||
|
},
|
||||||
|
"status": "ok"
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
MEMORY_RETRIEVED
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"cadence": "daily",
|
||||||
|
"operation": "store",
|
||||||
|
"runId": "session-mluzfucz",
|
||||||
|
"servicePath": "src/services/memory/index.js#ingestEvents",
|
||||||
|
"inputs": {
|
||||||
|
"agentId": "test-audit-agent",
|
||||||
|
"promptPath": "",
|
||||||
|
"promptIntent": "scheduler memory store",
|
||||||
|
"events": 2,
|
||||||
|
"sourceFile": "fallback"
|
||||||
|
},
|
||||||
|
"outputs": {
|
||||||
|
"storedCount": 1,
|
||||||
|
"summaries": [
|
||||||
|
"Store memory event A for daily :: scheduler memory store Store memory event B for daily :: scheduler memory store"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"status": "ok"
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
MEMORY_STORED
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"cadence": "daily",
|
||||||
|
"operation": "retrieve",
|
||||||
|
"runId": "session-mluzkanb",
|
||||||
|
"servicePath": "src/services/memory/index.js#getRelevantMemories",
|
||||||
|
"inputs": {
|
||||||
|
"agentId": "todo-triage-agent",
|
||||||
|
"promptPath": "",
|
||||||
|
"promptIntent": "scheduler memory retrieval",
|
||||||
|
"query": "scheduler | daily | agent | todo-triage-agent | intent | scheduler memory retrieval",
|
||||||
|
"events": 1
|
||||||
|
},
|
||||||
|
"outputs": {
|
||||||
|
"ingestedCount": 1,
|
||||||
|
"retrievedCount": 1
|
||||||
|
},
|
||||||
|
"status": "ok"
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
MEMORY_RETRIEVED
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"cadence": "daily",
|
||||||
|
"operation": "store",
|
||||||
|
"runId": "session-mluzkknh",
|
||||||
|
"servicePath": "src/services/memory/index.js#ingestEvents",
|
||||||
|
"inputs": {
|
||||||
|
"agentId": "todo-triage-agent",
|
||||||
|
"promptPath": "",
|
||||||
|
"promptIntent": "scheduler memory store",
|
||||||
|
"events": 2,
|
||||||
|
"sourceFile": "fallback"
|
||||||
|
},
|
||||||
|
"outputs": {
|
||||||
|
"storedCount": 1,
|
||||||
|
"summaries": [
|
||||||
|
"Store memory event A for daily :: scheduler memory store Store memory event B for daily :: scheduler memory store"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"status": "ok"
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
MEMORY_STORED
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"cadence": "daily",
|
||||||
|
"operation": "retrieve",
|
||||||
|
"runId": "session-mluzsu17",
|
||||||
|
"servicePath": "src/services/memory/index.js#getRelevantMemories",
|
||||||
|
"inputs": {
|
||||||
|
"agentId": "torch-garbage-collection-agent",
|
||||||
|
"promptPath": "",
|
||||||
|
"promptIntent": "scheduler memory retrieval",
|
||||||
|
"query": "scheduler | daily | agent | torch-garbage-collection-agent | intent | scheduler memory retrieval",
|
||||||
|
"events": 1
|
||||||
|
},
|
||||||
|
"outputs": {
|
||||||
|
"ingestedCount": 1,
|
||||||
|
"retrievedCount": 1
|
||||||
|
},
|
||||||
|
"status": "ok"
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
MEMORY_RETRIEVED
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"cadence": "daily",
|
||||||
|
"operation": "store",
|
||||||
|
"runId": "session-mluzt2t2",
|
||||||
|
"servicePath": "src/services/memory/index.js#ingestEvents",
|
||||||
|
"inputs": {
|
||||||
|
"agentId": "torch-garbage-collection-agent",
|
||||||
|
"promptPath": "",
|
||||||
|
"promptIntent": "scheduler memory store",
|
||||||
|
"events": 2,
|
||||||
|
"sourceFile": "fallback"
|
||||||
|
},
|
||||||
|
"outputs": {
|
||||||
|
"storedCount": 1,
|
||||||
|
"summaries": [
|
||||||
|
"Store memory event A for daily :: scheduler memory store Store memory event B for daily :: scheduler memory store"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"status": "ok"
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
MEMORY_STORED
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"cadence": "daily",
|
||||||
|
"operation": "retrieve",
|
||||||
|
"runId": "session-mlv03xay",
|
||||||
|
"servicePath": "src/services/memory/index.js#getRelevantMemories",
|
||||||
|
"inputs": {
|
||||||
|
"agentId": "audit-agent",
|
||||||
|
"promptPath": "",
|
||||||
|
"promptIntent": "scheduler memory retrieval",
|
||||||
|
"query": "scheduler | daily | agent | audit-agent | intent | scheduler memory retrieval",
|
||||||
|
"events": 1
|
||||||
|
},
|
||||||
|
"outputs": {
|
||||||
|
"ingestedCount": 1,
|
||||||
|
"retrievedCount": 1
|
||||||
|
},
|
||||||
|
"status": "ok"
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
MEMORY_RETRIEVED
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"cadence": "daily",
|
||||||
|
"operation": "store",
|
||||||
|
"runId": "session-mlv0498w",
|
||||||
|
"servicePath": "src/services/memory/index.js#ingestEvents",
|
||||||
|
"inputs": {
|
||||||
|
"agentId": "audit-agent",
|
||||||
|
"promptPath": "",
|
||||||
|
"promptIntent": "scheduler memory store",
|
||||||
|
"events": 2,
|
||||||
|
"sourceFile": "fallback"
|
||||||
|
},
|
||||||
|
"outputs": {
|
||||||
|
"storedCount": 1,
|
||||||
|
"summaries": [
|
||||||
|
"Store memory event A for daily :: scheduler memory store Store memory event B for daily :: scheduler memory store"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"status": "ok"
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
MEMORY_STORED
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"cadence": "daily",
|
||||||
|
"operation": "retrieve",
|
||||||
|
"runId": "session-mlv16ngi",
|
||||||
|
"servicePath": "src/services/memory/index.js#getRelevantMemories",
|
||||||
|
"inputs": {
|
||||||
|
"agentId": "ci-health-agent",
|
||||||
|
"promptPath": "",
|
||||||
|
"promptIntent": "scheduler memory retrieval",
|
||||||
|
"query": "scheduler | daily | agent | ci-health-agent | intent | scheduler memory retrieval",
|
||||||
|
"events": 1
|
||||||
|
},
|
||||||
|
"outputs": {
|
||||||
|
"ingestedCount": 1,
|
||||||
|
"retrievedCount": 1
|
||||||
|
},
|
||||||
|
"status": "ok"
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
MEMORY_RETRIEVED
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"cadence": "daily",
|
||||||
|
"operation": "store",
|
||||||
|
"runId": "session-mlv16wbs",
|
||||||
|
"servicePath": "src/services/memory/index.js#ingestEvents",
|
||||||
|
"inputs": {
|
||||||
|
"agentId": "ci-health-agent",
|
||||||
|
"promptPath": "",
|
||||||
|
"promptIntent": "scheduler memory store",
|
||||||
|
"events": 2,
|
||||||
|
"sourceFile": "fallback"
|
||||||
|
},
|
||||||
|
"outputs": {
|
||||||
|
"storedCount": 1,
|
||||||
|
"summaries": [
|
||||||
|
"Store memory event A for daily :: scheduler memory store Store memory event B for daily :: scheduler memory store"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"status": "ok"
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
MEMORY_STORED
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"cadence": "daily",
|
||||||
|
"operation": "retrieve",
|
||||||
|
"runId": "session-mlv1h4b5",
|
||||||
|
"servicePath": "src/services/memory/index.js#getRelevantMemories",
|
||||||
|
"inputs": {
|
||||||
|
"agentId": "const-refactor-agent",
|
||||||
|
"promptPath": "",
|
||||||
|
"promptIntent": "scheduler memory retrieval",
|
||||||
|
"query": "scheduler | daily | agent | const-refactor-agent | intent | scheduler memory retrieval",
|
||||||
|
"events": 1
|
||||||
|
},
|
||||||
|
"outputs": {
|
||||||
|
"ingestedCount": 1,
|
||||||
|
"retrievedCount": 1
|
||||||
|
},
|
||||||
|
"status": "ok"
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
MEMORY_RETRIEVED
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"cadence": "daily",
|
||||||
|
"operation": "store",
|
||||||
|
"runId": "session-mlv1hbp7",
|
||||||
|
"servicePath": "src/services/memory/index.js#ingestEvents",
|
||||||
|
"inputs": {
|
||||||
|
"agentId": "const-refactor-agent",
|
||||||
|
"promptPath": "",
|
||||||
|
"promptIntent": "scheduler memory store",
|
||||||
|
"events": 2,
|
||||||
|
"sourceFile": "fallback"
|
||||||
|
},
|
||||||
|
"outputs": {
|
||||||
|
"storedCount": 1,
|
||||||
|
"summaries": [
|
||||||
|
"Store memory event A for daily :: scheduler memory store Store memory event B for daily :: scheduler memory store"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"status": "ok"
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
MEMORY_STORED
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
---
|
||||||
|
agent: style-agent
|
||||||
|
cadence: daily
|
||||||
|
platform: qwen-code
|
||||||
|
status: failed
|
||||||
|
failure_category: execution_error
|
||||||
|
timestamp: 2026-02-20T14:16:47Z
|
||||||
|
lock_event_id: c8e8381ce5bf9cb55b51a81f0e14cafc2ce6b080424af43b6e51190bab8306f3
|
||||||
|
---
|
||||||
|
|
||||||
|
# style-agent Daily Run — Failed
|
||||||
|
|
||||||
|
## Failure Summary
|
||||||
|
|
||||||
|
**Validation command failed:** `npm run lint` (in torch/ subdirectory)
|
||||||
|
|
||||||
|
**Exit code:** 2
|
||||||
|
|
||||||
|
**Reason:** ESLint configuration file missing. The lint command exited with a configuration error, not a code quality issue.
|
||||||
|
|
||||||
|
## Error Details
|
||||||
|
|
||||||
|
```
|
||||||
|
> [email protected] lint
|
||||||
|
> eslint .
|
||||||
|
|
||||||
|
Oops! Something went wrong! :(
|
||||||
|
|
||||||
|
ESLint: 10.0.0
|
||||||
|
|
||||||
|
ESLint couldn't find an eslint.config.(js|mjs|cjs) file.
|
||||||
|
|
||||||
|
From ESLint v9.0.0, the default configuration file is now eslint.config.js.
|
||||||
|
```
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
- **Agent selected:** style-agent (via roster round-robin after scheduler-update-agent)
|
||||||
|
- **Excluded agents:** scheduler-update-agent (locked by same agent in previous run)
|
||||||
|
- **Lock acquired:** Yes (event ID: c8e8381ce5bf9cb55b51a81f0e14cafc2ce6b080424af43b6e51190bab8306f3)
|
||||||
|
- **Memory retrieval:** Completed successfully (MEMORY_RETRIEVED)
|
||||||
|
- **Memory storage:** Completed successfully (MEMORY_STORED)
|
||||||
|
- **Validation failure:** Repository lint infrastructure not configured (missing eslint.config.js)
|
||||||
|
|
||||||
|
## Agent Execution Notes
|
||||||
|
|
||||||
|
The style-agent prompt requires `format` and `lint` scripts in package.json. The root package.json does not define these scripts. The torch/ subdirectory has a lint script but lacks the required ESLint v9+ configuration file.
|
||||||
|
|
||||||
|
Per style-agent.md "Failure modes" section:
|
||||||
|
> - Missing/undefined scripts in `package.json`.
|
||||||
|
> Open an issue including: exact failing command(s), error output snippet, files involved, recommended next step.
|
||||||
|
|
||||||
|
## Next Steps / Retry Guidance
|
||||||
|
|
||||||
|
1. **Add ESLint configuration:** Create `torch/eslint.config.js` or `torch/eslint.config.mjs` with ESLint v9+ flat config format.
|
||||||
|
|
||||||
|
2. **Alternative:** If linting is not required for this repository, remove the lint validation step from the scheduler cycle or mark it as optional.
|
||||||
|
|
||||||
|
3. **For style-agent work:** The root repository would need `format` and `lint` scripts defined in the root package.json for the style-agent to execute its workflow.
|
||||||
|
|
||||||
|
## Scheduler Flow Compliance
|
||||||
|
|
||||||
|
- Step 1-8: Completed (preflight, lock acquisition, memory retrieval/storage)
|
||||||
|
- Step 9 (validation): **Failed** — lint exited non-zero
|
||||||
|
- Step 10 (lock:complete): **Skipped** — per scheduler-flow.md, completion publish is forbidden when validation fails
|
||||||
|
- Step 11: Writing _failed.md (this file)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*This failure log was written by the daily scheduler. The lock remains active until TTL expiration.*
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
---
|
||||||
|
agent: test-audit-agent
|
||||||
|
cadence: daily
|
||||||
|
platform: qwen-code
|
||||||
|
status: failed
|
||||||
|
failure_category: execution_error
|
||||||
|
timestamp: 2026-02-20T14:26:16Z
|
||||||
|
lock_event_id: 255d7212a7dad5f8fe626f8ded2017d9b3c5d48d578ed69ecea3e20a72b6ba49
|
||||||
|
---
|
||||||
|
|
||||||
|
# test-audit-agent Daily Run — Failed
|
||||||
|
|
||||||
|
## Failure Summary
|
||||||
|
|
||||||
|
**Validation command failed:** `npm run lint` (in torch/ subdirectory)
|
||||||
|
|
||||||
|
**Exit code:** 2
|
||||||
|
|
||||||
|
**Reason:** ESLint configuration file missing. The lint command exited with a configuration error, not a code quality issue.
|
||||||
|
|
||||||
|
## Error Details
|
||||||
|
|
||||||
|
```
|
||||||
|
> [email protected] lint
|
||||||
|
> eslint .
|
||||||
|
|
||||||
|
Oops! Something went wrong! :(
|
||||||
|
|
||||||
|
ESLint: 10.0.0
|
||||||
|
|
||||||
|
ESLint couldn't find an eslint.config.(js|mjs|cjs) file.
|
||||||
|
|
||||||
|
From ESLint v9.0.0, the default configuration file is now eslint.config.js.
|
||||||
|
```
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
- **Agent selected:** test-audit-agent (via roster round-robin after style-agent)
|
||||||
|
- **Excluded agents:** scheduler-update-agent, style-agent (both locked)
|
||||||
|
- **Lock acquired:** Yes (event ID: 255d7212a7dad5f8fe626f8ded2017d9b3c5d48d578ed69ecea3e20a72b6ba49)
|
||||||
|
- **Memory retrieval:** Completed successfully (MEMORY_RETRIEVED)
|
||||||
|
- **Memory storage:** Completed successfully (MEMORY_STORED)
|
||||||
|
- **Agent work:** No test files found in repository; agent exited without changes per prompt guidance
|
||||||
|
- **Validation failure:** Repository lint infrastructure not configured (missing eslint.config.js)
|
||||||
|
|
||||||
|
## Agent Execution Notes
|
||||||
|
|
||||||
|
The test-audit-agent prompt states: "If no work is required, exit without making changes."
|
||||||
|
|
||||||
|
Repository inspection found:
|
||||||
|
- No test files in root repository (package.json test script echoes error)
|
||||||
|
- Test audit tools exist in torch/scripts/test-audit/ but no tests to audit
|
||||||
|
|
||||||
|
## Next Steps / Retry Guidance
|
||||||
|
|
||||||
|
1. **Add ESLint configuration:** Create `torch/eslint.config.js` or `torch/eslint.config.mjs` with ESLint v9+ flat config format.
|
||||||
|
|
||||||
|
2. **Alternative:** If linting is not required for this repository, remove the lint validation step from the scheduler cycle or mark it as optional.
|
||||||
|
|
||||||
|
## Scheduler Flow Compliance
|
||||||
|
|
||||||
|
- Step 1-8: Completed (preflight, lock acquisition, memory retrieval/storage, evidence validation)
|
||||||
|
- Step 9 (validation): **Failed** — lint exited non-zero
|
||||||
|
- Step 10 (lock:complete): **Skipped** — per scheduler-flow.md, completion publish is forbidden when validation fails
|
||||||
|
- Step 11: Writing _failed.md (this file)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*This failure log was written by the daily scheduler. The lock remains active until TTL expiration.*
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
---
|
||||||
|
agent: todo-triage-agent
|
||||||
|
cadence: daily
|
||||||
|
platform: qwen-code
|
||||||
|
status: failed
|
||||||
|
failure_category: execution_error
|
||||||
|
timestamp: 2026-02-20T14:29:58Z
|
||||||
|
lock_event_id: 18c93e4a5476427bae5d77e3944ca4a9fc8bf504b0bee2890a37422c2afdded5
|
||||||
|
---
|
||||||
|
|
||||||
|
# todo-triage-agent Daily Run — Failed
|
||||||
|
|
||||||
|
## Failure Summary
|
||||||
|
|
||||||
|
**Validation command failed:** `npm run lint` (in torch/ subdirectory)
|
||||||
|
|
||||||
|
**Exit code:** 2
|
||||||
|
|
||||||
|
**Reason:** ESLint configuration file missing. The lint command exited with a configuration error, not a code quality issue.
|
||||||
|
|
||||||
|
## Error Details
|
||||||
|
|
||||||
|
```
|
||||||
|
> [email protected] lint
|
||||||
|
> eslint .
|
||||||
|
|
||||||
|
Oops! Something went wrong! :(
|
||||||
|
|
||||||
|
ESLint: 10.0.0
|
||||||
|
|
||||||
|
ESLint couldn't find an eslint.config.(js|mjs|cjs) file.
|
||||||
|
|
||||||
|
From ESLint v9.0.0, the default configuration file is now eslint.config.js.
|
||||||
|
```
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
- **Agent selected:** todo-triage-agent (via roster round-robin after test-audit-agent)
|
||||||
|
- **Excluded agents:** scheduler-update-agent, style-agent, test-audit-agent (all locked)
|
||||||
|
- **Lock acquired:** Yes (event ID: 18c93e4a5476427bae5d77e3944ca4a9fc8bf504b0bee2890a37422c2afdded5)
|
||||||
|
- **Memory retrieval:** Completed successfully (MEMORY_RETRIEVED)
|
||||||
|
- **Memory storage:** Completed successfully (MEMORY_STORED)
|
||||||
|
- **Agent work:** No actionable TODOs found in repository (only 1 config prefix marker)
|
||||||
|
- **Validation failure:** Repository lint infrastructure not configured (missing eslint.config.js)
|
||||||
|
|
||||||
|
## Agent Execution Notes
|
||||||
|
|
||||||
|
The todo-triage-agent scanned for TODO/FIXME/XXX markers:
|
||||||
|
- Found 1 match: `torch/scripts/agent/verify-run-artifacts.mjs:15` (config prefix `expectedPrefix: 'TODO_'`, not an actual TODO)
|
||||||
|
- No actionable TODOs requiring triage
|
||||||
|
- Per prompt guidance: "If no work is required, exit without making changes."
|
||||||
|
|
||||||
|
## Next Steps / Retry Guidance
|
||||||
|
|
||||||
|
1. **Add ESLint configuration:** Create `torch/eslint.config.js` or `torch/eslint.config.mjs` with ESLint v9+ flat config format.
|
||||||
|
|
||||||
|
2. **Alternative:** If linting is not required for this repository, remove the lint validation step from the scheduler cycle or mark it as optional.
|
||||||
|
|
||||||
|
## Scheduler Flow Compliance
|
||||||
|
|
||||||
|
- Step 1-8: Completed (preflight, lock acquisition, memory retrieval/storage, evidence validation)
|
||||||
|
- Step 9 (validation): **Failed** — lint exited non-zero
|
||||||
|
- Step 10 (lock:complete): **Skipped** — per scheduler-flow.md, completion publish is forbidden when validation fails
|
||||||
|
- Step 11: Writing _failed.md (this file)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*This failure log was written by the daily scheduler. The lock remains active until TTL expiration.*
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
---
|
||||||
|
agent: torch-garbage-collection-agent
|
||||||
|
cadence: daily
|
||||||
|
platform: qwen-code
|
||||||
|
status: failed
|
||||||
|
failure_category: execution_error
|
||||||
|
timestamp: 2026-02-20T14:36:33Z
|
||||||
|
lock_event_id: cfcb929b090e97e9b96544869639f2a0787f1bb131b2c4d402c707d30d815c9e
|
||||||
|
---
|
||||||
|
|
||||||
|
# torch-garbage-collection-agent Daily Run — Failed
|
||||||
|
|
||||||
|
## Failure Summary
|
||||||
|
|
||||||
|
**Validation command failed:** `npm run lint` (in torch/ subdirectory)
|
||||||
|
|
||||||
|
**Exit code:** 2
|
||||||
|
|
||||||
|
**Reason:** ESLint configuration file missing. The lint command exited with a configuration error, not a code quality issue.
|
||||||
|
|
||||||
|
## Error Details
|
||||||
|
|
||||||
|
```
|
||||||
|
> [email protected] lint
|
||||||
|
> eslint .
|
||||||
|
|
||||||
|
Oops! Something went wrong! :(
|
||||||
|
|
||||||
|
ESLint: 10.0.0
|
||||||
|
|
||||||
|
ESLint couldn't find an eslint.config.(js|mjs|cjs) file.
|
||||||
|
|
||||||
|
From ESLint v9.0.0, the default configuration file is now eslint.config.js.
|
||||||
|
```
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
- **Agent selected:** torch-garbage-collection-agent (via roster round-robin after todo-triage-agent)
|
||||||
|
- **Excluded agents:** scheduler-update-agent, style-agent, test-audit-agent, todo-triage-agent (all locked)
|
||||||
|
- **Lock acquired:** Yes (event ID: cfcb929b090e97e9b96544869639f2a0787f1bb131b2c4d402c707d30d815c9e)
|
||||||
|
- **Memory retrieval:** Completed successfully (MEMORY_RETRIEVED)
|
||||||
|
- **Memory storage:** Completed successfully (MEMORY_STORED)
|
||||||
|
- **Agent work:** No stale log files found (files older than 14 days); no changes needed per prompt
|
||||||
|
- **Validation failure:** Repository lint infrastructure not configured (missing eslint.config.js)
|
||||||
|
|
||||||
|
## Agent Execution Notes
|
||||||
|
|
||||||
|
The torch-garbage-collection-agent scanned for stale files:
|
||||||
|
- Searched for: `*.log`, `*.log.*`, `*.out.log`, `./memory-updates/*.md` with mtime > 14 days
|
||||||
|
- Result: No stale files found
|
||||||
|
- Per prompt guidance: "If list is empty: report 'No stale files found' and stop."
|
||||||
|
|
||||||
|
## Next Steps / Retry Guidance
|
||||||
|
|
||||||
|
1. **Add ESLint configuration:** Create `torch/eslint.config.js` or `torch/eslint.config.mjs` with ESLint v9+ flat config format.
|
||||||
|
|
||||||
|
2. **Alternative:** If linting is not required for this repository, remove the lint validation step from the scheduler cycle or mark it as optional.
|
||||||
|
|
||||||
|
## Scheduler Flow Compliance
|
||||||
|
|
||||||
|
- Step 1-8: Completed (preflight, lock acquisition, memory retrieval/storage, evidence validation)
|
||||||
|
- Step 9 (validation): **Failed** — lint exited non-zero
|
||||||
|
- Step 10 (lock:complete): **Skipped** — per scheduler-flow.md, completion publish is forbidden when validation fails
|
||||||
|
- Step 11: Writing _failed.md (this file)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*This failure log was written by the daily scheduler. The lock remains active until TTL expiration.*
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
---
|
||||||
|
agent: audit-agent
|
||||||
|
cadence: daily
|
||||||
|
platform: qwen-code
|
||||||
|
status: failed
|
||||||
|
failure_category: execution_error
|
||||||
|
timestamp: 2026-02-20T14:45:13Z
|
||||||
|
lock_event_id: 4e4a12b34be821831fe64fe440f39238cff582cca743a7479fbfcf854987b190
|
||||||
|
---
|
||||||
|
|
||||||
|
# audit-agent Daily Run — Failed
|
||||||
|
|
||||||
|
## Failure Summary
|
||||||
|
|
||||||
|
**Validation command failed:** `npm run lint` (in torch/ subdirectory)
|
||||||
|
|
||||||
|
**Exit code:** 2
|
||||||
|
|
||||||
|
**Reason:** ESLint configuration file missing. The lint command exited with a configuration error, not a code quality issue.
|
||||||
|
|
||||||
|
## Error Details
|
||||||
|
|
||||||
|
```
|
||||||
|
> [email protected] lint
|
||||||
|
> eslint .
|
||||||
|
|
||||||
|
Oops! Something went wrong! :(
|
||||||
|
|
||||||
|
ESLint: 10.0.0
|
||||||
|
|
||||||
|
ESLint couldn't find an eslint.config.(js|mjs|cjs) file.
|
||||||
|
|
||||||
|
From ESLint v9.0.0, the default configuration file is now eslint.config.js.
|
||||||
|
```
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
- **Agent selected:** audit-agent (via roster round-robin after torch-garbage-collection-agent, wrapping to index 0)
|
||||||
|
- **Excluded agents:** scheduler-update-agent, style-agent, test-audit-agent, todo-triage-agent, torch-garbage-collection-agent (all locked)
|
||||||
|
- **Lock acquired:** Yes (event ID: 4e4a12b34be821831fe64fe440f39238cff582cca743a7479fbfcf854987b190)
|
||||||
|
- **Memory retrieval:** Completed successfully (MEMORY_RETRIEVED)
|
||||||
|
- **Memory storage:** Completed successfully (MEMORY_STORED)
|
||||||
|
- **Agent work:** Audit scripts not found in repository (`scripts/check-file-size.mjs`, `scripts/check-innerhtml.mjs` missing); skipped per prompt guidance
|
||||||
|
- **Validation failure:** Repository lint infrastructure not configured (missing eslint.config.js)
|
||||||
|
|
||||||
|
## Agent Execution Notes
|
||||||
|
|
||||||
|
The audit-agent requires the following scripts which do not exist in this repository:
|
||||||
|
- `scripts/check-file-size.mjs` — NOT FOUND
|
||||||
|
- `scripts/check-innerhtml.mjs` — NOT FOUND
|
||||||
|
|
||||||
|
Per audit-agent.md failure modes: "If specific resources (files, URLs) are unavailable, log the error and skip."
|
||||||
|
|
||||||
|
## Next Steps / Retry Guidance
|
||||||
|
|
||||||
|
1. **Add ESLint configuration:** Create `torch/eslint.config.js` or `torch/eslint.config.mjs` with ESLint v9+ flat config format.
|
||||||
|
|
||||||
|
2. **Alternative:** If linting is not required for this repository, remove the lint validation step from the scheduler cycle or mark it as optional.
|
||||||
|
|
||||||
|
3. **For audit-agent work:** The repository would need the audit scripts (`scripts/check-file-size.mjs`, `scripts/check-innerhtml.mjs`) to be added for the agent to execute its workflow.
|
||||||
|
|
||||||
|
## Scheduler Flow Compliance
|
||||||
|
|
||||||
|
- Step 1-8: Completed (preflight, lock acquisition, memory retrieval/storage, evidence validation)
|
||||||
|
- Step 9 (validation): **Failed** — lint exited non-zero
|
||||||
|
- Step 10 (lock:complete): **Skipped** — per scheduler-flow.md, completion publish is forbidden when validation fails
|
||||||
|
- Step 11: Writing _failed.md (this file)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*This failure log was written by the daily scheduler. The lock remains active until TTL expiration.*
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
---
|
||||||
|
agent: ci-health-agent
|
||||||
|
cadence: daily
|
||||||
|
platform: qwen-code
|
||||||
|
status: completed
|
||||||
|
timestamp: 2026-02-20T15:15:44Z
|
||||||
|
lock_event_id: 536172682a928268c1be9adf11f751cd67c1f185fb2b8f6d2e4e356d8ec7b073
|
||||||
|
completion_event_id: ceefcfbab98f06ee008de1bda721af63fa4084ef40845f6f746a2cee6f569973
|
||||||
|
---
|
||||||
|
|
||||||
|
# ci-health-agent Daily Run — Completed
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
**Status:** Success
|
||||||
|
|
||||||
|
**Agent:** ci-health-agent
|
||||||
|
|
||||||
|
**Prompt:** torch/src/prompts/daily/ci-health-agent.md
|
||||||
|
|
||||||
|
## Execution Summary
|
||||||
|
|
||||||
|
### Agent Work
|
||||||
|
|
||||||
|
The ci-health-agent inspected the repository for CI health issues:
|
||||||
|
|
||||||
|
1. **CI Workflows:** No `.github/workflows/` directory found — CI not yet configured
|
||||||
|
2. **Unit Tests:** All tests pass (`npm run test:unit:lock-backend` — 4/4 tests passing)
|
||||||
|
3. **Lint:** All checks pass (`npm run lint` — 0 errors)
|
||||||
|
|
||||||
|
**Conclusion:** No flaky tests or CI issues identified. Repository is in good health. No changes required per prompt guidance: "If no work is required, exit without making changes."
|
||||||
|
|
||||||
|
### Memory Workflow
|
||||||
|
|
||||||
|
- **Retrieval:** Completed successfully (`MEMORY_RETRIEVED`)
|
||||||
|
- **Storage:** Completed successfully (`MEMORY_STORED`)
|
||||||
|
- **Evidence:** retrieve.ok ✓, store.ok ✓
|
||||||
|
|
||||||
|
### Validation
|
||||||
|
|
||||||
|
- **Lint:** Passed (0 errors)
|
||||||
|
- **Completion Publish:** Successful (event ID: `ceefcfbab98f06ee008de1bda721af63fa4084ef40845f6f746a2cee6f569973`)
|
||||||
|
|
||||||
|
## Learnings
|
||||||
|
|
||||||
|
No learnings recorded (memory storage ran with fallback placeholder).
|
||||||
|
|
||||||
|
## Scheduler Flow Compliance
|
||||||
|
|
||||||
|
- Step 1-8: Completed (preflight, lock acquisition, memory retrieval/storage, evidence validation)
|
||||||
|
- Step 9 (validation): **Passed** — lint exited 0
|
||||||
|
- Step 10 (lock:complete): **Completed** — published successfully
|
||||||
|
- Step 11: Writing _completed.md (this file)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*This completion log was written by the daily scheduler.*
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
---
|
||||||
|
agent: const-refactor-agent
|
||||||
|
cadence: daily
|
||||||
|
platform: qwen-code
|
||||||
|
status: completed
|
||||||
|
timestamp: 2026-02-20T15:23:32Z
|
||||||
|
lock_event_id: 3327522086c83e33b527acca300fdb183a73640ed29329f8f10cfbc8e1432366
|
||||||
|
completion_event_id: 30e5f8e7722e77e6e9d63b1b6d21a8e48814b73bf00382781ffa5a8079e6062b
|
||||||
|
---
|
||||||
|
|
||||||
|
# const-refactor-agent Daily Run — Completed
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
**Status:** Success
|
||||||
|
|
||||||
|
**Agent:** const-refactor-agent
|
||||||
|
|
||||||
|
**Prompt:** torch/src/prompts/daily/const-refactor-agent.md
|
||||||
|
|
||||||
|
## Execution Summary
|
||||||
|
|
||||||
|
### Agent Work
|
||||||
|
|
||||||
|
The const-refactor-agent inspected the repository for duplicate numeric constants:
|
||||||
|
|
||||||
|
1. **Searched for numeric literals** in `src/**/*.mjs` files
|
||||||
|
2. **Reviewed existing constants** in `src/constants.mjs`:
|
||||||
|
- `DEFAULT_TTL = 7200`
|
||||||
|
- `DEFAULT_QUERY_TIMEOUT_MS = 30_000`
|
||||||
|
- `DEFAULT_PUBLISH_TIMEOUT_MS = 15_000`
|
||||||
|
- `DEFAULT_RETRY_ATTEMPTS = 4`
|
||||||
|
- `DEFAULT_RETRY_BASE_DELAY_MS = 500`
|
||||||
|
- `DEFAULT_QUARANTINE_COOLDOWN_MS = 30_000`
|
||||||
|
- `DEFAULT_SNAPSHOT_INTERVAL_MS = 60_000`
|
||||||
|
- And many more well-named constants
|
||||||
|
|
||||||
|
**Conclusion:** The codebase already follows best practices for constants. All numeric values are properly extracted to `src/constants.mjs` with semantic naming (e.g., `DEFAULT_QUERY_TIMEOUT_MS`, `RACE_CHECK_DELAY_MS`). No duplicate numeric literals found in source code that need refactoring.
|
||||||
|
|
||||||
|
Per prompt guidance: "If no work is required, exit without making changes."
|
||||||
|
|
||||||
|
### Memory Workflow
|
||||||
|
|
||||||
|
- **Retrieval:** Completed successfully (`MEMORY_RETRIEVED`)
|
||||||
|
- **Storage:** Completed successfully (`MEMORY_STORED`)
|
||||||
|
- **Evidence:** retrieve.ok ✓, store.ok ✓
|
||||||
|
|
||||||
|
### Validation
|
||||||
|
|
||||||
|
- **Lint:** Passed (0 errors)
|
||||||
|
- **Completion Publish:** Successful (event ID: `30e5f8e7722e77e6e9d63b1b6d21a8e48814b73bf00382781ffa5a8079e6062b`)
|
||||||
|
|
||||||
|
## Learnings
|
||||||
|
|
||||||
|
No learnings recorded (memory storage ran with fallback placeholder).
|
||||||
|
|
||||||
|
## Scheduler Flow Compliance
|
||||||
|
|
||||||
|
- Step 1-8: Completed (preflight, lock acquisition, memory retrieval/storage, evidence validation)
|
||||||
|
- Step 9 (validation): **Passed** — lint exited 0
|
||||||
|
- Step 10 (lock:complete): **Completed** — published successfully
|
||||||
|
- Step 11: Writing _completed.md (this file)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*This completion log was written by the daily scheduler.*
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
import js from '@eslint/js';
|
||||||
|
import globals from 'globals';
|
||||||
|
|
||||||
|
export default [
|
||||||
|
js.configs.recommended,
|
||||||
|
{
|
||||||
|
languageOptions: {
|
||||||
|
ecmaVersion: 2024,
|
||||||
|
sourceType: 'module',
|
||||||
|
globals: {
|
||||||
|
...globals.node,
|
||||||
|
...globals.es2024,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
// Basic code quality
|
||||||
|
'no-unused-vars': ['error', { argsIgnorePattern: '^_', varsIgnorePattern: '^_' }],
|
||||||
|
'no-console': 'off',
|
||||||
|
'no-debugger': 'warn',
|
||||||
|
|
||||||
|
// Style preferences
|
||||||
|
'indent': ['error', 2],
|
||||||
|
'quotes': ['error', 'single', { avoidEscape: true }],
|
||||||
|
'semi': ['error', 'always'],
|
||||||
|
'comma-dangle': ['error', 'always-multiline'],
|
||||||
|
'no-trailing-spaces': 'error',
|
||||||
|
'eol-last': 'error',
|
||||||
|
|
||||||
|
// Best practices
|
||||||
|
'eqeqeq': ['error', 'always'],
|
||||||
|
'no-var': 'error',
|
||||||
|
'prefer-const': 'error',
|
||||||
|
'no-implied-eval': 'error',
|
||||||
|
'no-new-func': 'error',
|
||||||
|
'no-return-await': 'error',
|
||||||
|
|
||||||
|
// Error handling
|
||||||
|
'no-throw-literal': 'error',
|
||||||
|
'prefer-promise-reject-errors': 'error',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Ignore patterns
|
||||||
|
ignores: [
|
||||||
|
'node_modules/**',
|
||||||
|
'dist/**',
|
||||||
|
'build/**',
|
||||||
|
'coverage/**',
|
||||||
|
'.scheduler-memory/**',
|
||||||
|
'task-logs/**',
|
||||||
|
'dashboard/**',
|
||||||
|
'landing/**',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
Generated
+1036
File diff suppressed because it is too large.
Load diff
@@ -46,9 +46,9 @@ const LOG_FILE = 'artifacts/debug_frontend.log';
|
|||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
log(`Navigating to http://localhost:4173/dashboard/...`);
|
log('Navigating to http://localhost:4173/dashboard/...');
|
||||||
await page.goto('http://localhost:4173/dashboard/', { waitUntil: 'networkidle', timeout: 10000 });
|
await page.goto('http://localhost:4173/dashboard/', { waitUntil: 'networkidle', timeout: 10000 });
|
||||||
log(`Navigation complete.`);
|
log('Navigation complete.');
|
||||||
// Give it a bit more time for any delayed errors
|
// Give it a bit more time for any delayed errors
|
||||||
await page.waitForTimeout(2000);
|
await page.waitForTimeout(2000);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -64,7 +64,7 @@ const LOG_FILE = 'artifacts/debug_frontend.log';
|
|||||||
console.error(`\nFound ${errors.length} errors.`);
|
console.error(`\nFound ${errors.length} errors.`);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
} else {
|
} else {
|
||||||
console.log(`\nNo errors found.`);
|
console.log('\nNo errors found.');
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
@@ -91,7 +91,7 @@ function generateEvent() {
|
|||||||
pubkey: randomString(64), // Mock Pubkey
|
pubkey: randomString(64), // Mock Pubkey
|
||||||
created_at: Math.floor(Date.now() / 1000),
|
created_at: Math.floor(Date.now() / 1000),
|
||||||
tags: tags,
|
tags: tags,
|
||||||
content: content
|
content: content,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ const stats = {
|
|||||||
latencyMax: 0,
|
latencyMax: 0,
|
||||||
errors: {},
|
errors: {},
|
||||||
startTime: Date.now(),
|
startTime: Date.now(),
|
||||||
endTime: 0
|
endTime: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
async function sleep(ms) {
|
async function sleep(ms) {
|
||||||
@@ -103,35 +103,35 @@ async function main() {
|
|||||||
|
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
const timer = setInterval(async () => {
|
const timer = setInterval(async () => {
|
||||||
if (Date.now() >= end) {
|
if (Date.now() >= end) {
|
||||||
clearInterval(timer);
|
clearInterval(timer);
|
||||||
pool.close([RELAY_URL]);
|
pool.close([RELAY_URL]);
|
||||||
stats.endTime = Date.now();
|
stats.endTime = Date.now();
|
||||||
writeReport(reportDir);
|
writeReport(reportDir);
|
||||||
resolve();
|
resolve();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const clientIdx = Math.floor(Math.random() * CLIENTS);
|
const clientIdx = Math.floor(Math.random() * CLIENTS);
|
||||||
const sk = sks[clientIdx];
|
const sk = sks[clientIdx];
|
||||||
const event = generateEvent(sk);
|
const event = generateEvent(sk);
|
||||||
|
|
||||||
stats.totalAttempts++;
|
stats.totalAttempts++;
|
||||||
const sendStart = Date.now();
|
const sendStart = Date.now();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await Promise.any(pool.publish([RELAY_URL], event));
|
await Promise.any(pool.publish([RELAY_URL], event));
|
||||||
const latency = Date.now() - sendStart;
|
const latency = Date.now() - sendStart;
|
||||||
stats.success++;
|
stats.success++;
|
||||||
stats.latencySum += latency;
|
stats.latencySum += latency;
|
||||||
stats.latencyMin = Math.min(stats.latencyMin, latency);
|
stats.latencyMin = Math.min(stats.latencyMin, latency);
|
||||||
stats.latencyMax = Math.max(stats.latencyMax, latency);
|
stats.latencyMax = Math.max(stats.latencyMax, latency);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
stats.failed++;
|
stats.failed++;
|
||||||
const msg = err.message || String(err);
|
const msg = err.message || String(err);
|
||||||
stats.errors[msg] = (stats.errors[msg] || 0) + 1;
|
stats.errors[msg] = (stats.errors[msg] || 0) + 1;
|
||||||
}
|
}
|
||||||
}, interval);
|
}, interval);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,7 +147,7 @@ function writeReport(dir) {
|
|||||||
durationSec: DURATION_SEC,
|
durationSec: DURATION_SEC,
|
||||||
rateEps: RATE_EPS,
|
rateEps: RATE_EPS,
|
||||||
mix: MIX,
|
mix: MIX,
|
||||||
dryRun: DRY_RUN
|
dryRun: DRY_RUN,
|
||||||
},
|
},
|
||||||
results: {
|
results: {
|
||||||
totalAttempts: stats.totalAttempts,
|
totalAttempts: stats.totalAttempts,
|
||||||
@@ -157,11 +157,11 @@ function writeReport(dir) {
|
|||||||
latency: {
|
latency: {
|
||||||
min: stats.latencyMin === Infinity ? 0 : stats.latencyMin,
|
min: stats.latencyMin === Infinity ? 0 : stats.latencyMin,
|
||||||
max: stats.latencyMax,
|
max: stats.latencyMax,
|
||||||
avg: avgLatency
|
avg: avgLatency,
|
||||||
},
|
},
|
||||||
errors: stats.errors
|
errors: stats.errors,
|
||||||
},
|
},
|
||||||
timestamp: new Date().toISOString()
|
timestamp: new Date().toISOString(),
|
||||||
};
|
};
|
||||||
|
|
||||||
const dateStr = new Date().toISOString().slice(0, 10);
|
const dateStr = new Date().toISOString().slice(0, 10);
|
||||||
|
|||||||
@@ -435,10 +435,10 @@ async function main() {
|
|||||||
const excluded = Array.isArray(checkPayload.excluded)
|
const excluded = Array.isArray(checkPayload.excluded)
|
||||||
? checkPayload.excluded
|
? checkPayload.excluded
|
||||||
: [...new Set([
|
: [...new Set([
|
||||||
...(Array.isArray(checkPayload.locked) ? checkPayload.locked : []),
|
...(Array.isArray(checkPayload.locked) ? checkPayload.locked : []),
|
||||||
...(Array.isArray(checkPayload.paused) ? checkPayload.paused : []),
|
...(Array.isArray(checkPayload.paused) ? checkPayload.paused : []),
|
||||||
...(Array.isArray(checkPayload.completed) ? checkPayload.completed : []),
|
...(Array.isArray(checkPayload.completed) ? checkPayload.completed : []),
|
||||||
])];
|
])];
|
||||||
const excludedSet = new Set(excluded);
|
const excludedSet = new Set(excluded);
|
||||||
|
|
||||||
const latestFile = await getLatestFile(logDir);
|
const latestFile = await getLatestFile(logDir);
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ function resolveRunner(promptMarkdown) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (platform === 'linux') {
|
if (platform === 'linux') {
|
||||||
return { command: 'echo', args: [`[scheduler] Platform 'linux' detected. simulated execution for prompt.`] };
|
return { command: 'echo', args: ['[scheduler] Platform \'linux\' detected. simulated execution for prompt.'] };
|
||||||
}
|
}
|
||||||
|
|
||||||
fail(`Unsupported AGENT_PLATFORM="${platform}". Set SCHEDULER_AGENT_RUNNER_COMMAND to override.`);
|
fail(`Unsupported AGENT_PLATFORM="${platform}". Set SCHEDULER_AGENT_RUNNER_COMMAND to override.`);
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ async function waitForServer(url, timeoutMs = 10000) {
|
|||||||
try {
|
try {
|
||||||
const res = await fetch(url);
|
const res = await fetch(url);
|
||||||
if (res.ok) return true;
|
if (res.ok) return true;
|
||||||
} catch (_e) {
|
} catch {
|
||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
await sleep(500);
|
await sleep(500);
|
||||||
@@ -63,7 +63,7 @@ async function main() {
|
|||||||
const results = {
|
const results = {
|
||||||
timestamp: new Date().toISOString(),
|
timestamp: new Date().toISOString(),
|
||||||
steps: [],
|
steps: [],
|
||||||
success: false
|
success: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
let serverProcess = null;
|
let serverProcess = null;
|
||||||
@@ -133,7 +133,7 @@ async function main() {
|
|||||||
},
|
},
|
||||||
oneose() {
|
oneose() {
|
||||||
sub.close();
|
sub.close();
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// Wait for event
|
// Wait for event
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ async function listRecentFiles(dirPath, sinceMs) {
|
|||||||
if (!entry.isFile()) continue;
|
if (!entry.isFile()) continue;
|
||||||
const filePath = path.join(dirPath, entry.name);
|
const filePath = path.join(dirPath, entry.name);
|
||||||
const stat = await fs.stat(filePath);
|
const stat = await fs.stat(filePath);
|
||||||
if (sinceMs != null && stat.mtimeMs < sinceMs) continue;
|
if (sinceMs !== null && stat.mtimeMs < sinceMs) continue;
|
||||||
files.push({ name: entry.name, filePath, mtimeMs: stat.mtimeMs });
|
files.push({ name: entry.name, filePath, mtimeMs: stat.mtimeMs });
|
||||||
}
|
}
|
||||||
return files.sort((a, b) => a.mtimeMs - b.mtimeMs);
|
return files.sort((a, b) => a.mtimeMs - b.mtimeMs);
|
||||||
@@ -305,7 +305,7 @@ async function main() {
|
|||||||
const activePromptPath = args.promptPath || process.env.SCHEDULER_PROMPT_PATH || null;
|
const activePromptPath = args.promptPath || process.env.SCHEDULER_PROMPT_PATH || null;
|
||||||
const activeRunStart = args.runStart || null;
|
const activeRunStart = args.runStart || null;
|
||||||
|
|
||||||
if (args.since && sinceMs == null) {
|
if (args.since && sinceMs === null) {
|
||||||
console.error(`Invalid --since value: ${args.since}`);
|
console.error(`Invalid --since value: ${args.since}`);
|
||||||
process.exit(2);
|
process.exit(2);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ async function waitForServer() {
|
|||||||
req.end();
|
req.end();
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
} catch (_e) {
|
} catch {
|
||||||
await sleep(100);
|
await sleep(100);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -43,7 +43,7 @@ function makeRequest() {
|
|||||||
async function benchmark() {
|
async function benchmark() {
|
||||||
console.log('Starting server...');
|
console.log('Starting server...');
|
||||||
const serverProcess = spawn('node', ['bin/torch-lock.mjs', 'dashboard', '--port', String(PORT)], {
|
const serverProcess = spawn('node', ['bin/torch-lock.mjs', 'dashboard', '--port', String(PORT)], {
|
||||||
stdio: 'ignore' // Suppress logs for cleaner output
|
stdio: 'ignore', // Suppress logs for cleaner output
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -54,20 +54,20 @@ async function benchmark() {
|
|||||||
|
|
||||||
let sent = 0;
|
let sent = 0;
|
||||||
const worker = async () => {
|
const worker = async () => {
|
||||||
while (true) {
|
while (true) {
|
||||||
const current = sent++;
|
const current = sent++;
|
||||||
if (current >= REQUESTS) break;
|
if (current >= REQUESTS) break;
|
||||||
try {
|
try {
|
||||||
await makeRequest();
|
await makeRequest();
|
||||||
} catch (_e) {
|
} catch {
|
||||||
// console.error(_e);
|
// console.error(_e);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const workers = [];
|
const workers = [];
|
||||||
for (let i = 0; i < CONCURRENCY; i++) {
|
for (let i = 0; i < CONCURRENCY; i++) {
|
||||||
workers.push(worker());
|
workers.push(worker());
|
||||||
}
|
}
|
||||||
|
|
||||||
await Promise.all(workers);
|
await Promise.all(workers);
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ async function runBenchmark() {
|
|||||||
const condensedCount = plan.condensedGroups.length;
|
const condensedCount = plan.condensedGroups.length;
|
||||||
// We expect exactly GROUPS condensed groups.
|
// We expect exactly GROUPS condensed groups.
|
||||||
if (condensedCount !== GROUPS) {
|
if (condensedCount !== GROUPS) {
|
||||||
console.warn(`Warning: Expected ${GROUPS} condensed groups, but got ${condensedCount}. Check grouping logic.`);
|
console.warn(`Warning: Expected ${GROUPS} condensed groups, but got ${condensedCount}. Check grouping logic.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
times.push(duration);
|
times.push(duration);
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ Options:
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (['.js', '.mjs', '.ts', '.html', '.css', '.json', '.md'].includes(path.extname(item))) {
|
if (['.js', '.mjs', '.ts', '.html', '.css', '.json', '.md'].includes(path.extname(item))) {
|
||||||
files.push(fullPath);
|
files.push(fullPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,14 +69,14 @@ Options:
|
|||||||
|
|
||||||
if (stat.isDirectory()) {
|
if (stat.isDirectory()) {
|
||||||
if (!EXCLUDED_DIRS.includes(item)) {
|
if (!EXCLUDED_DIRS.includes(item)) {
|
||||||
files = files.concat(scanDirectory(fullPath));
|
files = files.concat(scanDirectory(fullPath));
|
||||||
}
|
}
|
||||||
} else if (stat.isFile()) {
|
} else if (stat.isFile()) {
|
||||||
if (EXCLUDED_FILES.includes(item)) {
|
if (EXCLUDED_FILES.includes(item)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (TARGET_EXTENSIONS.includes(path.extname(item))) {
|
if (TARGET_EXTENSIONS.includes(path.extname(item))) {
|
||||||
files.push(fullPath);
|
files.push(fullPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,20 +13,20 @@ const runId = process.env.SCHEDULER_RUN_ID ||
|
|||||||
`session-${Date.now().toString(36)}`;
|
`session-${Date.now().toString(36)}`;
|
||||||
|
|
||||||
// Extract prompt intent
|
// Extract prompt intent
|
||||||
let promptIntent = `scheduler memory retrieval`;
|
let promptIntent = 'scheduler memory retrieval';
|
||||||
if (promptPath) {
|
if (promptPath) {
|
||||||
try {
|
try {
|
||||||
const promptRaw = readFileSync(promptPath, 'utf8');
|
const promptRaw = readFileSync(promptPath, 'utf8');
|
||||||
const promptLines = promptRaw.split(/\r?\n/).map(line => line.trim()).filter(Boolean);
|
const promptLines = promptRaw.split(/\r?\n/).map(line => line.trim()).filter(Boolean);
|
||||||
const intentLine = promptLines.find(line => line.startsWith('#') || line.toLowerCase().startsWith('goal'));
|
const intentLine = promptLines.find(line => line.startsWith('#') || line.toLowerCase().startsWith('goal'));
|
||||||
if (intentLine) {
|
if (intentLine) {
|
||||||
promptIntent = intentLine.replace(/^#+\s*/, '');
|
promptIntent = intentLine.replace(/^#+\s*/, '');
|
||||||
} else if (promptLines.length > 0) {
|
} else if (promptLines.length > 0) {
|
||||||
promptIntent = promptLines[0];
|
promptIntent = promptLines[0];
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.warn(`Could not read prompt file: ${promptPath}`, err.message);
|
|
||||||
}
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.warn(`Could not read prompt file: ${promptPath}`, err.message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Construct query
|
// Construct query
|
||||||
@@ -35,60 +35,60 @@ const timestamp = Date.now();
|
|||||||
|
|
||||||
// Create retrieval seed event
|
// Create retrieval seed event
|
||||||
const events = [{
|
const events = [{
|
||||||
agent_id: agentId,
|
agent_id: agentId,
|
||||||
content: `Memory retrieval seed for ${cadence} :: ${promptIntent}`,
|
content: `Memory retrieval seed for ${cadence} :: ${promptIntent}`,
|
||||||
timestamp,
|
timestamp,
|
||||||
tags: ['scheduler', cadence, 'retrieve'],
|
tags: ['scheduler', cadence, 'retrieve'],
|
||||||
metadata: {
|
metadata: {
|
||||||
session_id: runId,
|
session_id: runId,
|
||||||
source: 'scheduler-retrieve',
|
source: 'scheduler-retrieve',
|
||||||
importance: 0.4,
|
importance: 0.4,
|
||||||
prompt_path: promptPath
|
prompt_path: promptPath,
|
||||||
}
|
},
|
||||||
}];
|
}];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 1. Ingest seed event
|
// 1. Ingest seed event
|
||||||
const ingested = await ingestEvents(events, { agent_id: agentId });
|
const ingested = await ingestEvents(events, { agent_id: agentId });
|
||||||
|
|
||||||
// 2. Retrieve memories
|
// 2. Retrieve memories
|
||||||
const retrieved = await getRelevantMemories({ agent_id: agentId, query, k: 5 });
|
const retrieved = await getRelevantMemories({ agent_id: agentId, query, k: 5 });
|
||||||
|
|
||||||
// 3. Prepare artifacts
|
// 3. Prepare artifacts
|
||||||
const sessionDir = path.join('.scheduler-memory', runId);
|
const sessionDir = path.join('.scheduler-memory', runId);
|
||||||
const latestDir = path.join('.scheduler-memory', 'latest', cadence);
|
const latestDir = path.join('.scheduler-memory', 'latest', cadence);
|
||||||
|
|
||||||
mkdirSync(sessionDir, { recursive: true });
|
mkdirSync(sessionDir, { recursive: true });
|
||||||
mkdirSync(latestDir, { recursive: true });
|
mkdirSync(latestDir, { recursive: true });
|
||||||
|
|
||||||
const artifact = {
|
const artifact = {
|
||||||
cadence,
|
cadence,
|
||||||
operation: 'retrieve',
|
operation: 'retrieve',
|
||||||
runId,
|
runId,
|
||||||
servicePath: 'src/services/memory/index.js#getRelevantMemories',
|
servicePath: 'src/services/memory/index.js#getRelevantMemories',
|
||||||
inputs: {
|
inputs: {
|
||||||
agentId,
|
agentId,
|
||||||
promptPath,
|
promptPath,
|
||||||
promptIntent,
|
promptIntent,
|
||||||
query,
|
query,
|
||||||
events: events.length
|
events: events.length,
|
||||||
},
|
},
|
||||||
outputs: {
|
outputs: {
|
||||||
ingestedCount: ingested.length,
|
ingestedCount: ingested.length,
|
||||||
retrievedCount: retrieved.length
|
retrievedCount: retrieved.length,
|
||||||
},
|
},
|
||||||
status: 'ok'
|
status: 'ok',
|
||||||
};
|
};
|
||||||
|
|
||||||
// 4. Write artifacts
|
// 4. Write artifacts
|
||||||
writeFileSync(path.join(sessionDir, 'retrieve.json'), JSON.stringify(artifact, null, 2));
|
writeFileSync(path.join(sessionDir, 'retrieve.json'), JSON.stringify(artifact, null, 2));
|
||||||
writeFileSync(path.join(sessionDir, 'retrieve.ok'), 'MEMORY_RETRIEVED\n');
|
writeFileSync(path.join(sessionDir, 'retrieve.ok'), 'MEMORY_RETRIEVED\n');
|
||||||
writeFileSync(path.join(latestDir, 'retrieve.ok'), 'MEMORY_RETRIEVED\n');
|
writeFileSync(path.join(latestDir, 'retrieve.ok'), 'MEMORY_RETRIEVED\n');
|
||||||
writeFileSync(path.join(latestDir, 'memories.md'), retrieved.map(m => `- ${m.content}`).join('\n'));
|
writeFileSync(path.join(latestDir, 'memories.md'), retrieved.map(m => `- ${m.content}`).join('\n'));
|
||||||
|
|
||||||
// 5. Output success marker to stdout (for scheduler verification)
|
// 5. Output success marker to stdout (for scheduler verification)
|
||||||
console.log('MEMORY_RETRIEVED');
|
console.log('MEMORY_RETRIEVED');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Memory retrieval failed:', error);
|
console.error('Memory retrieval failed:', error);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
@@ -13,20 +13,20 @@ const runId = process.env.SCHEDULER_RUN_ID ||
|
|||||||
`session-${Date.now().toString(36)}`;
|
`session-${Date.now().toString(36)}`;
|
||||||
|
|
||||||
// Extract prompt intent
|
// Extract prompt intent
|
||||||
let promptIntent = `scheduler memory store`;
|
let promptIntent = 'scheduler memory store';
|
||||||
if (promptPath) {
|
if (promptPath) {
|
||||||
try {
|
try {
|
||||||
const promptRaw = readFileSync(promptPath, 'utf8');
|
const promptRaw = readFileSync(promptPath, 'utf8');
|
||||||
const promptLines = promptRaw.split(/\r?\n/).map(line => line.trim()).filter(Boolean);
|
const promptLines = promptRaw.split(/\r?\n/).map(line => line.trim()).filter(Boolean);
|
||||||
const intentLine = promptLines.find(line => line.startsWith('#') || line.toLowerCase().startsWith('goal'));
|
const intentLine = promptLines.find(line => line.startsWith('#') || line.toLowerCase().startsWith('goal'));
|
||||||
if (intentLine) {
|
if (intentLine) {
|
||||||
promptIntent = intentLine.replace(/^#+\s*/, '');
|
promptIntent = intentLine.replace(/^#+\s*/, '');
|
||||||
} else if (promptLines.length > 0) {
|
} else if (promptLines.length > 0) {
|
||||||
promptIntent = promptLines[0];
|
promptIntent = promptLines[0];
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.warn(`Could not read prompt file: ${promptPath}`, err.message);
|
|
||||||
}
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.warn(`Could not read prompt file: ${promptPath}`, err.message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for memory input file
|
// Check for memory input file
|
||||||
@@ -40,103 +40,103 @@ const defaultFile = 'memory-update.md';
|
|||||||
const targetFile = explicitFile || (envFile && existsSync(envFile) ? envFile : (existsSync(defaultFile) ? defaultFile : null));
|
const targetFile = explicitFile || (envFile && existsSync(envFile) ? envFile : (existsSync(defaultFile) ? defaultFile : null));
|
||||||
|
|
||||||
if (targetFile) {
|
if (targetFile) {
|
||||||
try {
|
try {
|
||||||
if (existsSync(targetFile)) {
|
if (existsSync(targetFile)) {
|
||||||
memoryContent = readFileSync(targetFile, 'utf8').trim();
|
memoryContent = readFileSync(targetFile, 'utf8').trim();
|
||||||
console.log(`Loaded memory content from ${targetFile}`);
|
console.log(`Loaded memory content from ${targetFile}`);
|
||||||
} else {
|
} else {
|
||||||
console.warn(`Target memory file does not exist: ${targetFile}`);
|
console.warn(`Target memory file does not exist: ${targetFile}`);
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.warn(`Failed to read memory file ${targetFile}:`, err.message);
|
|
||||||
}
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.warn(`Failed to read memory file ${targetFile}:`, err.message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const baseTs = Date.now();
|
const baseTs = Date.now();
|
||||||
let events = [];
|
let events = [];
|
||||||
|
|
||||||
if (memoryContent) {
|
if (memoryContent) {
|
||||||
events.push({
|
events.push({
|
||||||
agent_id: agentId,
|
agent_id: agentId,
|
||||||
content: memoryContent,
|
content: memoryContent,
|
||||||
timestamp: baseTs,
|
timestamp: baseTs,
|
||||||
tags: ['scheduler', cadence, 'store', 'insight'],
|
tags: ['scheduler', cadence, 'store', 'insight'],
|
||||||
metadata: {
|
metadata: {
|
||||||
session_id: runId,
|
session_id: runId,
|
||||||
source: 'agent-output',
|
source: 'agent-output',
|
||||||
importance: 0.8, // User-provided memory is important
|
importance: 0.8, // User-provided memory is important
|
||||||
prompt_path: promptPath
|
prompt_path: promptPath,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// Fallback if no file provided (preserve existing behavior for compatibility)
|
// Fallback if no file provided (preserve existing behavior for compatibility)
|
||||||
console.warn(`No memory input found (checked --file, env SCHEDULER_MEMORY_FILE=${envFile}, or default ${defaultFile}). using fallback placeholder.`);
|
console.warn(`No memory input found (checked --file, env SCHEDULER_MEMORY_FILE=${envFile}, or default ${defaultFile}). using fallback placeholder.`);
|
||||||
events = [
|
events = [
|
||||||
{
|
{
|
||||||
agent_id: agentId,
|
agent_id: agentId,
|
||||||
content: `Store memory event A for ${cadence} :: ${promptIntent}`,
|
content: `Store memory event A for ${cadence} :: ${promptIntent}`,
|
||||||
timestamp: baseTs,
|
timestamp: baseTs,
|
||||||
tags: ['scheduler', cadence, 'store'],
|
tags: ['scheduler', cadence, 'store'],
|
||||||
metadata: {
|
metadata: {
|
||||||
session_id: runId,
|
session_id: runId,
|
||||||
source: 'scheduler-store',
|
source: 'scheduler-store',
|
||||||
importance: 0.55,
|
importance: 0.55,
|
||||||
prompt_path: promptPath
|
prompt_path: promptPath,
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
agent_id: agentId,
|
agent_id: agentId,
|
||||||
content: `Store memory event B for ${cadence} :: ${promptIntent}`,
|
content: `Store memory event B for ${cadence} :: ${promptIntent}`,
|
||||||
timestamp: baseTs + 1,
|
timestamp: baseTs + 1,
|
||||||
tags: ['scheduler', cadence, 'store'],
|
tags: ['scheduler', cadence, 'store'],
|
||||||
metadata: {
|
metadata: {
|
||||||
session_id: runId,
|
session_id: runId,
|
||||||
source: 'scheduler-store',
|
source: 'scheduler-store',
|
||||||
importance: 0.55,
|
importance: 0.55,
|
||||||
prompt_path: promptPath
|
prompt_path: promptPath,
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 1. Ingest events
|
// 1. Ingest events
|
||||||
const stored = await ingestEvents(events, { agent_id: agentId });
|
const stored = await ingestEvents(events, { agent_id: agentId });
|
||||||
|
|
||||||
// 2. Prepare artifacts
|
// 2. Prepare artifacts
|
||||||
const sessionDir = path.join('.scheduler-memory', runId);
|
const sessionDir = path.join('.scheduler-memory', runId);
|
||||||
const latestDir = path.join('.scheduler-memory', 'latest', cadence);
|
const latestDir = path.join('.scheduler-memory', 'latest', cadence);
|
||||||
|
|
||||||
mkdirSync(sessionDir, { recursive: true });
|
mkdirSync(sessionDir, { recursive: true });
|
||||||
mkdirSync(latestDir, { recursive: true });
|
mkdirSync(latestDir, { recursive: true });
|
||||||
|
|
||||||
const artifact = {
|
const artifact = {
|
||||||
cadence,
|
cadence,
|
||||||
operation: 'store',
|
operation: 'store',
|
||||||
runId,
|
runId,
|
||||||
servicePath: 'src/services/memory/index.js#ingestEvents',
|
servicePath: 'src/services/memory/index.js#ingestEvents',
|
||||||
inputs: {
|
inputs: {
|
||||||
agentId,
|
agentId,
|
||||||
promptPath,
|
promptPath,
|
||||||
promptIntent,
|
promptIntent,
|
||||||
events: events.length,
|
events: events.length,
|
||||||
sourceFile: targetFile || 'fallback'
|
sourceFile: targetFile || 'fallback',
|
||||||
},
|
},
|
||||||
outputs: {
|
outputs: {
|
||||||
storedCount: stored.length,
|
storedCount: stored.length,
|
||||||
summaries: stored.map(m => m.summary)
|
summaries: stored.map(m => m.summary),
|
||||||
},
|
},
|
||||||
status: 'ok'
|
status: 'ok',
|
||||||
};
|
};
|
||||||
|
|
||||||
// 3. Write artifacts
|
// 3. Write artifacts
|
||||||
writeFileSync(path.join(sessionDir, 'store.json'), JSON.stringify(artifact, null, 2));
|
writeFileSync(path.join(sessionDir, 'store.json'), JSON.stringify(artifact, null, 2));
|
||||||
writeFileSync(path.join(sessionDir, 'store.ok'), 'MEMORY_STORED\n');
|
writeFileSync(path.join(sessionDir, 'store.ok'), 'MEMORY_STORED\n');
|
||||||
writeFileSync(path.join(latestDir, 'store.ok'), 'MEMORY_STORED\n');
|
writeFileSync(path.join(latestDir, 'store.ok'), 'MEMORY_STORED\n');
|
||||||
|
|
||||||
// 4. Output success marker to stdout (for scheduler verification)
|
// 4. Output success marker to stdout (for scheduler verification)
|
||||||
console.log('MEMORY_STORED');
|
console.log('MEMORY_STORED');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Memory storage failed:', error);
|
console.error('Memory storage failed:', error);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
@@ -24,7 +24,7 @@ async function runTests(i) {
|
|||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
// Pass strictly the test args to the child process
|
// Pass strictly the test args to the child process
|
||||||
const child = spawn('node', ['--test', ...testArgs], {
|
const child = spawn('node', ['--test', ...testArgs], {
|
||||||
stdio: ['ignore', 'pipe', 'pipe']
|
stdio: ['ignore', 'pipe', 'pipe'],
|
||||||
});
|
});
|
||||||
|
|
||||||
let stdout = '';
|
let stdout = '';
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ function getFiles(dir, files = []) {
|
|||||||
files.push(name);
|
files.push(name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (_e) {
|
} catch {
|
||||||
// ignore if dir doesn't exist
|
// ignore if dir doesn't exist
|
||||||
}
|
}
|
||||||
return files;
|
return files;
|
||||||
@@ -45,11 +45,11 @@ for (const file of testFiles) {
|
|||||||
// Naive check for assertions: look for assert., expect(, t.
|
// Naive check for assertions: look for assert., expect(, t.
|
||||||
// Many tests use 't' context from node:test
|
// Many tests use 't' context from node:test
|
||||||
if (!content.includes('assert.') && !content.includes('expect(') && !content.includes('t.plan') && !content.includes('strictEqual')) {
|
if (!content.includes('assert.') && !content.includes('expect(') && !content.includes('t.plan') && !content.includes('strictEqual')) {
|
||||||
// This is very naive, might false positive on 't.ok' etc.
|
// This is very naive, might false positive on 't.ok' etc.
|
||||||
// Let's look for common assertion keywords.
|
// Let's look for common assertion keywords.
|
||||||
const assertionKeywords = ['assert', 'expect', 'strictEqual', 'deepStrictEqual', 'ok', 'equal'];
|
const assertionKeywords = ['assert', 'expect', 'strictEqual', 'deepStrictEqual', 'ok', 'equal'];
|
||||||
const hasAssertion = assertionKeywords.some(k => content.includes(k));
|
const hasAssertion = assertionKeywords.some(k => content.includes(k));
|
||||||
if (!hasAssertion) issues.push('No obvious assertions');
|
if (!hasAssertion) issues.push('No obvious assertions');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (issues.length > 0) {
|
if (issues.length > 0) {
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ const invariants = [
|
|||||||
'parseFrontmatterAgent(content) || parseAgentFromFilename(latestFile)',
|
'parseFrontmatterAgent(content) || parseAgentFromFilename(latestFile)',
|
||||||
'scheduler.firstPromptByCadence?.[cadence]',
|
'scheduler.firstPromptByCadence?.[cadence]',
|
||||||
'ALL_EXCLUDED_REASON',
|
'ALL_EXCLUDED_REASON',
|
||||||
"reason: ALL_EXCLUDED_REASON",
|
'reason: ALL_EXCLUDED_REASON',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -161,7 +161,7 @@ const [meta, flow, scheduler] = await Promise.all([
|
|||||||
const errors = [];
|
const errors = [];
|
||||||
|
|
||||||
for (const invariant of invariants) {
|
for (const invariant of invariants) {
|
||||||
if (invariant.promptSnippets?.meta) {
|
if (invariant.promptSnippets?.meta) {
|
||||||
const missingMeta = collectMissing(meta, invariant.promptSnippets.meta);
|
const missingMeta = collectMissing(meta, invariant.promptSnippets.meta);
|
||||||
for (const snippet of missingMeta) {
|
for (const snippet of missingMeta) {
|
||||||
errors.push(`[${invariant.name}] ${FILES.meta} missing snippet: ${snippet}`);
|
errors.push(`[${invariant.name}] ${FILES.meta} missing snippet: ${snippet}`);
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ export async function cmdBackup({ output = null } = {}) {
|
|||||||
|
|
||||||
await fs.writeFile(
|
await fs.writeFile(
|
||||||
path.join(backupDir, 'backup-manifest.json'),
|
path.join(backupDir, 'backup-manifest.json'),
|
||||||
JSON.stringify(manifest, null, 2)
|
JSON.stringify(manifest, null, 2),
|
||||||
);
|
);
|
||||||
|
|
||||||
console.log(JSON.stringify({ success: true, ...manifest }, null, 2));
|
console.log(JSON.stringify({ success: true, ...manifest }, null, 2));
|
||||||
|
|||||||
@@ -8,14 +8,14 @@ import { todayDateStr as _todayDateStr, nowUnix as _nowUnix } from './utils.mjs'
|
|||||||
|
|
||||||
export async function cmdList(cadence, deps = {}) {
|
export async function cmdList(cadence, deps = {}) {
|
||||||
const {
|
const {
|
||||||
getRelays = _getRelays,
|
getRelays = _getRelays,
|
||||||
getNamespace = _getNamespace,
|
getNamespace = _getNamespace,
|
||||||
getRoster = _getRoster,
|
getRoster = _getRoster,
|
||||||
queryLocks = _queryLocks,
|
queryLocks = _queryLocks,
|
||||||
todayDateStr = _todayDateStr,
|
todayDateStr = _todayDateStr,
|
||||||
nowUnix = _nowUnix,
|
nowUnix = _nowUnix,
|
||||||
log = console.log,
|
log = console.log,
|
||||||
error = console.error,
|
error = console.error,
|
||||||
} = deps;
|
} = deps;
|
||||||
|
|
||||||
const relays = await getRelays();
|
const relays = await getRelays();
|
||||||
|
|||||||
+25
-25
@@ -4,19 +4,19 @@ import fs from 'node:fs/promises';
|
|||||||
|
|
||||||
export async function cmdProposal(subcommand, args = {}) {
|
export async function cmdProposal(subcommand, args = {}) {
|
||||||
switch (subcommand) {
|
switch (subcommand) {
|
||||||
case 'create':
|
case 'create':
|
||||||
return await handleCreate(args);
|
return handleCreate(args);
|
||||||
case 'list':
|
case 'list':
|
||||||
return await handleList(args);
|
return handleList(args);
|
||||||
case 'apply':
|
case 'apply':
|
||||||
return await handleApply(args);
|
return handleApply(args);
|
||||||
case 'reject':
|
case 'reject':
|
||||||
return await handleReject(args);
|
return handleReject(args);
|
||||||
case 'show':
|
case 'show':
|
||||||
return await handleShow(args);
|
return handleShow(args);
|
||||||
default:
|
default:
|
||||||
console.error(`Unknown proposal subcommand: ${subcommand}`);
|
console.error(`Unknown proposal subcommand: ${subcommand}`);
|
||||||
throw new ExitError(1, 'Unknown subcommand');
|
throw new ExitError(1, 'Unknown subcommand');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29,7 +29,7 @@ async function handleCreate({ agent, target, contentFile, reason }) {
|
|||||||
let newContent;
|
let newContent;
|
||||||
try {
|
try {
|
||||||
newContent = await fs.readFile(contentFile, 'utf8');
|
newContent = await fs.readFile(contentFile, 'utf8');
|
||||||
} catch (_e) {
|
} catch {
|
||||||
console.error(`Failed to read content file: ${contentFile}`);
|
console.error(`Failed to read content file: ${contentFile}`);
|
||||||
throw new ExitError(1, 'File read error');
|
throw new ExitError(1, 'File read error');
|
||||||
}
|
}
|
||||||
@@ -85,15 +85,15 @@ async function handleReject({ id, reason }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function handleShow({ id }) {
|
async function handleShow({ id }) {
|
||||||
if (!id) {
|
if (!id) {
|
||||||
console.error('Usage: torch-lock proposal show --id <proposal-id>');
|
console.error('Usage: torch-lock proposal show --id <proposal-id>');
|
||||||
throw new ExitError(1, 'Missing id');
|
throw new ExitError(1, 'Missing id');
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const proposal = await getProposal(id);
|
const proposal = await getProposal(id);
|
||||||
console.log(JSON.stringify(proposal, null, 2));
|
console.log(JSON.stringify(proposal, null, 2));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(`Failed to show proposal: ${e.message}`);
|
console.error(`Failed to show proposal: ${e.message}`);
|
||||||
throw new ExitError(1, 'Show failed');
|
throw new ExitError(1, 'Show failed');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+138
-138
@@ -11,7 +11,7 @@ const SECURITY_HEADERS = {
|
|||||||
'Content-Security-Policy': "default-src 'self'; script-src 'self' https://cdn.jsdelivr.net; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; connect-src 'self' wss:; object-src 'none'; base-uri 'self';",
|
'Content-Security-Policy': "default-src 'self'; script-src 'self' https://cdn.jsdelivr.net; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; connect-src 'self' wss:; object-src 'none'; base-uri 'self';",
|
||||||
'X-Content-Type-Options': 'nosniff',
|
'X-Content-Type-Options': 'nosniff',
|
||||||
'X-Frame-Options': 'DENY',
|
'X-Frame-Options': 'DENY',
|
||||||
'Referrer-Policy': 'strict-origin-when-cross-origin'
|
'Referrer-Policy': 'strict-origin-when-cross-origin',
|
||||||
};
|
};
|
||||||
|
|
||||||
function timingSafeCompare(a, b) {
|
function timingSafeCompare(a, b) {
|
||||||
@@ -41,160 +41,160 @@ export async function cmdDashboard(port = DEFAULT_DASHBOARD_PORT, host = '127.0.
|
|||||||
const server = http.createServer(async (req, res) => {
|
const server = http.createServer(async (req, res) => {
|
||||||
try {
|
try {
|
||||||
// Basic Auth check
|
// Basic Auth check
|
||||||
if (auth) {
|
if (auth) {
|
||||||
const authHeader = req.headers.authorization;
|
const authHeader = req.headers.authorization;
|
||||||
if (!authHeader) {
|
if (!authHeader) {
|
||||||
res.writeHead(401, { ...SECURITY_HEADERS, 'WWW-Authenticate': 'Basic realm="TORCH Dashboard"' });
|
res.writeHead(401, { ...SECURITY_HEADERS, 'WWW-Authenticate': 'Basic realm="TORCH Dashboard"' });
|
||||||
res.end('Authentication required');
|
res.end('Authentication required');
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
const parts = authHeader.split(' ');
|
|
||||||
const type = parts[0];
|
|
||||||
const credentials = parts[1];
|
|
||||||
let isValid = false;
|
|
||||||
if (type === 'Basic' && credentials) {
|
|
||||||
try {
|
|
||||||
const decoded = Buffer.from(credentials, 'base64').toString();
|
|
||||||
isValid = timingSafeCompare(decoded, auth);
|
|
||||||
} catch {
|
|
||||||
isValid = false;
|
|
||||||
}
|
}
|
||||||
}
|
const parts = authHeader.split(' ');
|
||||||
|
const type = parts[0];
|
||||||
if (!isValid) {
|
const credentials = parts[1];
|
||||||
res.writeHead(401, { ...SECURITY_HEADERS, 'WWW-Authenticate': 'Basic realm="TORCH Dashboard"' });
|
let isValid = false;
|
||||||
res.end('Invalid credentials');
|
if (type === 'Basic' && credentials) {
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// URL parsing
|
|
||||||
const url = new URL(req.url, `http://${req.headers.host}`);
|
|
||||||
let pathname = url.pathname;
|
|
||||||
|
|
||||||
// Redirect / to /dashboard/
|
|
||||||
if (pathname === '/' || pathname === '/dashboard') {
|
|
||||||
res.writeHead(302, { ...SECURITY_HEADERS, 'Location': '/dashboard/' });
|
|
||||||
res.end();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function statSafe(p) {
|
|
||||||
try {
|
|
||||||
return await fsp.stat(p);
|
|
||||||
} catch {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Special case: /torch-config.json
|
|
||||||
// Priority: Env Var > User's CWD > Package default
|
|
||||||
if (pathname === '/torch-config.json') {
|
|
||||||
let configContent = null;
|
|
||||||
|
|
||||||
if (process.env.TORCH_CONFIG_PATH) {
|
|
||||||
const envPath = path.resolve(process.cwd(), process.env.TORCH_CONFIG_PATH);
|
|
||||||
try {
|
|
||||||
configContent = await fsp.readFile(envPath, 'utf8');
|
|
||||||
} catch {
|
|
||||||
// Explicitly provided config path not found, do not fall back
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const userConfigPath = path.resolve(process.cwd(), 'torch-config.json');
|
|
||||||
try {
|
|
||||||
configContent = await fsp.readFile(userConfigPath, 'utf8');
|
|
||||||
} catch {
|
|
||||||
// Try package default
|
|
||||||
const packageConfigPath = path.join(packageRoot, 'torch-config.json');
|
|
||||||
try {
|
try {
|
||||||
configContent = await fsp.readFile(packageConfigPath, 'utf8');
|
const decoded = Buffer.from(credentials, 'base64').toString();
|
||||||
|
isValid = timingSafeCompare(decoded, auth);
|
||||||
} catch {
|
} catch {
|
||||||
// Not found in either location
|
isValid = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!isValid) {
|
||||||
|
res.writeHead(401, { ...SECURITY_HEADERS, 'WWW-Authenticate': 'Basic realm="TORCH Dashboard"' });
|
||||||
|
res.end('Invalid credentials');
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (configContent) {
|
// URL parsing
|
||||||
try {
|
const url = new URL(req.url, `http://${req.headers.host}`);
|
||||||
const rawConfig = JSON.parse(configContent);
|
const pathname = url.pathname;
|
||||||
const safeConfig = parseTorchConfig(rawConfig);
|
|
||||||
|
|
||||||
// Security: Whitelist only fields required by the dashboard frontend
|
// Redirect / to /dashboard/
|
||||||
const publicConfig = {
|
if (pathname === '/' || pathname === '/dashboard') {
|
||||||
dashboard: safeConfig.dashboard ? { ...safeConfig.dashboard } : {},
|
res.writeHead(302, { ...SECURITY_HEADERS, 'Location': '/dashboard/' });
|
||||||
nostrLock: safeConfig.nostrLock
|
res.end();
|
||||||
? {
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function statSafe(p) {
|
||||||
|
try {
|
||||||
|
return await fsp.stat(p);
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Special case: /torch-config.json
|
||||||
|
// Priority: Env Var > User's CWD > Package default
|
||||||
|
if (pathname === '/torch-config.json') {
|
||||||
|
let configContent = null;
|
||||||
|
|
||||||
|
if (process.env.TORCH_CONFIG_PATH) {
|
||||||
|
const envPath = path.resolve(process.cwd(), process.env.TORCH_CONFIG_PATH);
|
||||||
|
try {
|
||||||
|
configContent = await fsp.readFile(envPath, 'utf8');
|
||||||
|
} catch {
|
||||||
|
// Explicitly provided config path not found, do not fall back
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const userConfigPath = path.resolve(process.cwd(), 'torch-config.json');
|
||||||
|
try {
|
||||||
|
configContent = await fsp.readFile(userConfigPath, 'utf8');
|
||||||
|
} catch {
|
||||||
|
// Try package default
|
||||||
|
const packageConfigPath = path.join(packageRoot, 'torch-config.json');
|
||||||
|
try {
|
||||||
|
configContent = await fsp.readFile(packageConfigPath, 'utf8');
|
||||||
|
} catch {
|
||||||
|
// Not found in either location
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (configContent) {
|
||||||
|
try {
|
||||||
|
const rawConfig = JSON.parse(configContent);
|
||||||
|
const safeConfig = parseTorchConfig(rawConfig);
|
||||||
|
|
||||||
|
// Security: Whitelist only fields required by the dashboard frontend
|
||||||
|
const publicConfig = {
|
||||||
|
dashboard: safeConfig.dashboard ? { ...safeConfig.dashboard } : {},
|
||||||
|
nostrLock: safeConfig.nostrLock
|
||||||
|
? {
|
||||||
namespace: safeConfig.nostrLock.namespace,
|
namespace: safeConfig.nostrLock.namespace,
|
||||||
relays: safeConfig.nostrLock.relays,
|
relays: safeConfig.nostrLock.relays,
|
||||||
}
|
}
|
||||||
: {},
|
: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
// Remove sensitive dashboard auth
|
// Remove sensitive dashboard auth
|
||||||
if (publicConfig.dashboard.auth) {
|
if (publicConfig.dashboard.auth) {
|
||||||
delete publicConfig.dashboard.auth;
|
delete publicConfig.dashboard.auth;
|
||||||
|
}
|
||||||
|
|
||||||
|
res.writeHead(200, { ...SECURITY_HEADERS, 'Content-Type': 'application/json' });
|
||||||
|
res.end(JSON.stringify(publicConfig, null, 2));
|
||||||
|
return;
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error parsing torch-config.json:', err);
|
||||||
|
res.writeHead(500, SECURITY_HEADERS);
|
||||||
|
res.end('Internal Server Error');
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
res.writeHead(200, { ...SECURITY_HEADERS, 'Content-Type': 'application/json' });
|
res.writeHead(404, SECURITY_HEADERS);
|
||||||
res.end(JSON.stringify(publicConfig, null, 2));
|
res.end('Not Found');
|
||||||
return;
|
|
||||||
} catch (err) {
|
|
||||||
console.error('Error parsing torch-config.json:', err);
|
|
||||||
res.writeHead(500, SECURITY_HEADERS);
|
|
||||||
res.end('Internal Server Error');
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
|
||||||
|
// Security check: prevent directory traversal
|
||||||
|
// Resolve path relative to packageRoot.
|
||||||
|
// We strip the leading slash from pathname (which comes from URL) to treat it as relative.
|
||||||
|
const relativePath = pathname.replace(/^\//, '');
|
||||||
|
let filePath = path.resolve(packageRoot, relativePath);
|
||||||
|
|
||||||
|
// Security check: restrict access to allowed paths
|
||||||
|
const allowedPaths = [
|
||||||
|
path.join(packageRoot, 'dashboard'),
|
||||||
|
path.join(packageRoot, 'assets'),
|
||||||
|
path.join(packageRoot, 'src', 'docs'),
|
||||||
|
path.join(packageRoot, 'src', 'constants.mjs'),
|
||||||
|
path.join(packageRoot, 'torch-config.json'),
|
||||||
|
];
|
||||||
|
|
||||||
|
const isAllowed = allowedPaths.some(allowedPath => {
|
||||||
|
const rel = path.relative(allowedPath, filePath);
|
||||||
|
return !rel.startsWith('..') && !path.isAbsolute(rel);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!isAllowed) {
|
||||||
|
res.writeHead(403, SECURITY_HEADERS);
|
||||||
|
res.end('Forbidden');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If directory, try index.html
|
||||||
|
let fileStat = await statSafe(filePath);
|
||||||
|
if (fileStat && fileStat.isDirectory()) {
|
||||||
|
filePath = path.join(filePath, 'index.html');
|
||||||
|
fileStat = await statSafe(filePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if file exists and is a file
|
||||||
|
if (!fileStat || !fileStat.isFile()) {
|
||||||
res.writeHead(404, SECURITY_HEADERS);
|
res.writeHead(404, SECURITY_HEADERS);
|
||||||
res.end('Not Found');
|
res.end('Not Found');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Security check: prevent directory traversal
|
// MIME types
|
||||||
// Resolve path relative to packageRoot.
|
const extname = path.extname(filePath);
|
||||||
// We strip the leading slash from pathname (which comes from URL) to treat it as relative.
|
let contentType = 'text/plain';
|
||||||
const relativePath = pathname.replace(/^\//, '');
|
switch (extname) {
|
||||||
let filePath = path.resolve(packageRoot, relativePath);
|
|
||||||
|
|
||||||
// Security check: restrict access to allowed paths
|
|
||||||
const allowedPaths = [
|
|
||||||
path.join(packageRoot, 'dashboard'),
|
|
||||||
path.join(packageRoot, 'assets'),
|
|
||||||
path.join(packageRoot, 'src', 'docs'),
|
|
||||||
path.join(packageRoot, 'src', 'constants.mjs'),
|
|
||||||
path.join(packageRoot, 'torch-config.json')
|
|
||||||
];
|
|
||||||
|
|
||||||
const isAllowed = allowedPaths.some(allowedPath => {
|
|
||||||
const rel = path.relative(allowedPath, filePath);
|
|
||||||
return !rel.startsWith('..') && !path.isAbsolute(rel);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!isAllowed) {
|
|
||||||
res.writeHead(403, SECURITY_HEADERS);
|
|
||||||
res.end('Forbidden');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If directory, try index.html
|
|
||||||
let fileStat = await statSafe(filePath);
|
|
||||||
if (fileStat && fileStat.isDirectory()) {
|
|
||||||
filePath = path.join(filePath, 'index.html');
|
|
||||||
fileStat = await statSafe(filePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if file exists and is a file
|
|
||||||
if (!fileStat || !fileStat.isFile()) {
|
|
||||||
res.writeHead(404, SECURITY_HEADERS);
|
|
||||||
res.end('Not Found');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// MIME types
|
|
||||||
const extname = path.extname(filePath);
|
|
||||||
let contentType = 'text/plain';
|
|
||||||
switch (extname) {
|
|
||||||
case '.html': contentType = 'text/html'; break;
|
case '.html': contentType = 'text/html'; break;
|
||||||
case '.js': contentType = 'text/javascript'; break;
|
case '.js': contentType = 'text/javascript'; break;
|
||||||
case '.mjs': contentType = 'text/javascript'; break;
|
case '.mjs': contentType = 'text/javascript'; break;
|
||||||
@@ -205,10 +205,10 @@ export async function cmdDashboard(port = DEFAULT_DASHBOARD_PORT, host = '127.0.
|
|||||||
case '.svg': contentType = 'image/svg+xml'; break;
|
case '.svg': contentType = 'image/svg+xml'; break;
|
||||||
case '.ico': contentType = 'image/x-icon'; break;
|
case '.ico': contentType = 'image/x-icon'; break;
|
||||||
case '.md': contentType = 'text/markdown'; break;
|
case '.md': contentType = 'text/markdown'; break;
|
||||||
}
|
}
|
||||||
|
|
||||||
res.writeHead(200, { ...SECURITY_HEADERS, 'Content-Type': contentType });
|
res.writeHead(200, { ...SECURITY_HEADERS, 'Content-Type': contentType });
|
||||||
fs.createReadStream(filePath).pipe(res);
|
fs.createReadStream(filePath).pipe(res);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Dashboard Server Error:', err);
|
console.error('Dashboard Server Error:', err);
|
||||||
if (!res.headersSent) {
|
if (!res.headersSent) {
|
||||||
|
|||||||
+179
-179
@@ -182,7 +182,7 @@ export async function cmdLock(agent, cadence, optionsOrDryRun = false, deps = {}
|
|||||||
raceCheckDelayMs = RACE_CHECK_DELAY_MS,
|
raceCheckDelayMs = RACE_CHECK_DELAY_MS,
|
||||||
getDateStr = todayDateStr,
|
getDateStr = todayDateStr,
|
||||||
log = console.log,
|
log = console.log,
|
||||||
error = console.error
|
error = console.error,
|
||||||
} = deps;
|
} = deps;
|
||||||
|
|
||||||
const relays = await getRelays();
|
const relays = await getRelays();
|
||||||
@@ -237,10 +237,10 @@ export async function cmdLock(agent, cadence, optionsOrDryRun = false, deps = {}
|
|||||||
|
|
||||||
// Check if it is a completed task
|
// Check if it is a completed task
|
||||||
if (earliest.status === 'completed') {
|
if (earliest.status === 'completed') {
|
||||||
error(`LOCK DENIED: Task already completed by event ${earliest.eventId}`);
|
error(`LOCK DENIED: Task already completed by event ${earliest.eventId}`);
|
||||||
log('LOCK_STATUS=denied');
|
log('LOCK_STATUS=denied');
|
||||||
log('LOCK_REASON=already_completed');
|
log('LOCK_REASON=already_completed');
|
||||||
throw new ExitError(3, 'Task already completed');
|
throw new ExitError(3, 'Task already completed');
|
||||||
}
|
}
|
||||||
|
|
||||||
error(
|
error(
|
||||||
@@ -349,7 +349,7 @@ export async function cmdList(cadence, deps = {}) {
|
|||||||
getRoster = _getRoster,
|
getRoster = _getRoster,
|
||||||
getDateStr = todayDateStr,
|
getDateStr = todayDateStr,
|
||||||
log = console.log,
|
log = console.log,
|
||||||
error = console.error
|
error = console.error,
|
||||||
} = deps;
|
} = deps;
|
||||||
|
|
||||||
const relays = await getRelays();
|
const relays = await getRelays();
|
||||||
@@ -383,10 +383,10 @@ export async function cmdList(cadence, deps = {}) {
|
|||||||
|
|
||||||
let remainMin = '?';
|
let remainMin = '?';
|
||||||
if (lock.status === 'completed') {
|
if (lock.status === 'completed') {
|
||||||
remainMin = 'done';
|
remainMin = 'done';
|
||||||
} else if (lock.expiresAt) {
|
} else if (lock.expiresAt) {
|
||||||
const remaining = lock.expiresAt - nowUnix();
|
const remaining = lock.expiresAt - nowUnix();
|
||||||
remainMin = Math.round(remaining / 60);
|
remainMin = Math.round(remaining / 60);
|
||||||
}
|
}
|
||||||
|
|
||||||
log(
|
log(
|
||||||
@@ -443,7 +443,7 @@ export async function cmdComplete(agent, cadence, optionsOrDryRun = false, deps
|
|||||||
finalizeEvent = _finalizeEvent,
|
finalizeEvent = _finalizeEvent,
|
||||||
getDateStr = todayDateStr,
|
getDateStr = todayDateStr,
|
||||||
log = console.log,
|
log = console.log,
|
||||||
error = console.error
|
error = console.error,
|
||||||
} = deps;
|
} = deps;
|
||||||
|
|
||||||
const relays = await getRelays();
|
const relays = await getRelays();
|
||||||
@@ -461,7 +461,7 @@ export async function cmdComplete(agent, cadence, optionsOrDryRun = false, deps
|
|||||||
|
|
||||||
if (!myLock) {
|
if (!myLock) {
|
||||||
error(`ERROR: No active lock found for agent "${agent}" on ${dateStr}.`);
|
error(`ERROR: No active lock found for agent "${agent}" on ${dateStr}.`);
|
||||||
error(`Cannot complete a task that is not locked or has already expired.`);
|
error('Cannot complete a task that is not locked or has already expired.');
|
||||||
throw new ExitError(1, 'No active lock found');
|
throw new ExitError(1, 'No active lock found');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -544,204 +544,204 @@ export async function main(argv) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch (args.command) {
|
switch (args.command) {
|
||||||
case 'check': {
|
case 'check': {
|
||||||
if (!args.cadence || !VALID_CADENCES.has(args.cadence)) {
|
if (!args.cadence || !VALID_CADENCES.has(args.cadence)) {
|
||||||
console.error(`ERROR: --cadence <${[...VALID_CADENCES].join('|')}> is required for check`);
|
console.error(`ERROR: --cadence <${[...VALID_CADENCES].join('|')}> is required for check`);
|
||||||
throw new ExitError(1, 'Missing cadence');
|
throw new ExitError(1, 'Missing cadence');
|
||||||
}
|
|
||||||
await cmdCheck(args.cadence, {
|
|
||||||
logDir: args.logDir,
|
|
||||||
ignoreLogs: args.ignoreLogs,
|
|
||||||
json: args.json,
|
|
||||||
jsonFile: args.jsonFile,
|
|
||||||
quiet: args.quiet,
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
await cmdCheck(args.cadence, {
|
||||||
|
logDir: args.logDir,
|
||||||
|
ignoreLogs: args.ignoreLogs,
|
||||||
|
json: args.json,
|
||||||
|
jsonFile: args.jsonFile,
|
||||||
|
quiet: args.quiet,
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case 'lock': {
|
case 'lock': {
|
||||||
if (!args.agent) {
|
if (!args.agent) {
|
||||||
console.error('ERROR: --agent <name> is required for lock');
|
console.error('ERROR: --agent <name> is required for lock');
|
||||||
throw new ExitError(1, 'Missing agent');
|
throw new ExitError(1, 'Missing agent');
|
||||||
}
|
|
||||||
if (!args.cadence || !VALID_CADENCES.has(args.cadence)) {
|
|
||||||
console.error(`ERROR: --cadence <${[...VALID_CADENCES].join('|')}> is required for lock`);
|
|
||||||
throw new ExitError(1, 'Missing cadence');
|
|
||||||
}
|
|
||||||
await cmdLock(args.agent, args.cadence, {
|
|
||||||
dryRun: args.dryRun,
|
|
||||||
platform: args.platform,
|
|
||||||
model: args.model
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
if (!args.cadence || !VALID_CADENCES.has(args.cadence)) {
|
||||||
case 'complete': {
|
console.error(`ERROR: --cadence <${[...VALID_CADENCES].join('|')}> is required for lock`);
|
||||||
if (!args.agent) {
|
throw new ExitError(1, 'Missing cadence');
|
||||||
console.error('ERROR: --agent <name> is required for complete');
|
|
||||||
throw new ExitError(1, 'Missing agent');
|
|
||||||
}
|
|
||||||
if (!args.cadence || !VALID_CADENCES.has(args.cadence)) {
|
|
||||||
console.error(`ERROR: --cadence <${[...VALID_CADENCES].join('|')}> is required for complete`);
|
|
||||||
throw new ExitError(1, 'Missing cadence');
|
|
||||||
}
|
|
||||||
await cmdComplete(args.agent, args.cadence, {
|
|
||||||
dryRun: args.dryRun,
|
|
||||||
platform: args.platform,
|
|
||||||
model: args.model
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
await cmdLock(args.agent, args.cadence, {
|
||||||
|
dryRun: args.dryRun,
|
||||||
|
platform: args.platform,
|
||||||
|
model: args.model,
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case 'list': {
|
case 'complete': {
|
||||||
if (args.cadence && !VALID_CADENCES.has(args.cadence)) {
|
if (!args.agent) {
|
||||||
console.error(`ERROR: --cadence must be one of: ${[...VALID_CADENCES].join(', ')}`);
|
console.error('ERROR: --agent <name> is required for complete');
|
||||||
throw new ExitError(1, 'Invalid cadence');
|
throw new ExitError(1, 'Missing agent');
|
||||||
}
|
|
||||||
await cmdList(args.cadence || null);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
if (!args.cadence || !VALID_CADENCES.has(args.cadence)) {
|
||||||
case 'health': {
|
console.error(`ERROR: --cadence <${[...VALID_CADENCES].join('|')}> is required for complete`);
|
||||||
if (!args.cadence || !VALID_CADENCES.has(args.cadence)) {
|
throw new ExitError(1, 'Missing cadence');
|
||||||
console.error(`ERROR: --cadence <${[...VALID_CADENCES].join('|')}> is required for health`);
|
|
||||||
throw new ExitError(1, 'Missing cadence');
|
|
||||||
}
|
|
||||||
const result = await runRelayHealthCheck({
|
|
||||||
cadence: args.cadence,
|
|
||||||
...(Number.isFinite(args.timeoutMs) ? { timeoutMs: args.timeoutMs } : {}),
|
|
||||||
...(Number.isFinite(args.allRelaysDownMinutes) ? { allRelaysDownMinutes: args.allRelaysDownMinutes } : {}),
|
|
||||||
...(Number.isFinite(args.minSuccessRate) ? { minSuccessRate: args.minSuccessRate } : {}),
|
|
||||||
...(Number.isFinite(args.windowMinutes) ? { windowMinutes: args.windowMinutes } : {}),
|
|
||||||
});
|
|
||||||
if (!result.ok) {
|
|
||||||
result.failureCategory = 'all relays unhealthy';
|
|
||||||
}
|
|
||||||
console.log(JSON.stringify(result, null, 2));
|
|
||||||
if (!result.ok) throw new ExitError(2, 'Relay health check failed');
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
await cmdComplete(args.agent, args.cadence, {
|
||||||
|
dryRun: args.dryRun,
|
||||||
|
platform: args.platform,
|
||||||
|
model: args.model,
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case 'dashboard': {
|
case 'list': {
|
||||||
await cmdDashboard(args.port, args.host);
|
if (args.cadence && !VALID_CADENCES.has(args.cadence)) {
|
||||||
break;
|
console.error(`ERROR: --cadence must be one of: ${[...VALID_CADENCES].join(', ')}`);
|
||||||
|
throw new ExitError(1, 'Invalid cadence');
|
||||||
}
|
}
|
||||||
|
await cmdList(args.cadence || null);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case 'init': {
|
case 'health': {
|
||||||
await cmdInit(args.force);
|
if (!args.cadence || !VALID_CADENCES.has(args.cadence)) {
|
||||||
break;
|
console.error(`ERROR: --cadence <${[...VALID_CADENCES].join('|')}> is required for health`);
|
||||||
|
throw new ExitError(1, 'Missing cadence');
|
||||||
}
|
}
|
||||||
|
const result = await runRelayHealthCheck({
|
||||||
case 'update': {
|
cadence: args.cadence,
|
||||||
await cmdUpdate(args.force);
|
...(Number.isFinite(args.timeoutMs) ? { timeoutMs: args.timeoutMs } : {}),
|
||||||
break;
|
...(Number.isFinite(args.allRelaysDownMinutes) ? { allRelaysDownMinutes: args.allRelaysDownMinutes } : {}),
|
||||||
|
...(Number.isFinite(args.minSuccessRate) ? { minSuccessRate: args.minSuccessRate } : {}),
|
||||||
|
...(Number.isFinite(args.windowMinutes) ? { windowMinutes: args.windowMinutes } : {}),
|
||||||
|
});
|
||||||
|
if (!result.ok) {
|
||||||
|
result.failureCategory = 'all relays unhealthy';
|
||||||
}
|
}
|
||||||
|
console.log(JSON.stringify(result, null, 2));
|
||||||
|
if (!result.ok) throw new ExitError(2, 'Relay health check failed');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case 'remove': {
|
case 'dashboard': {
|
||||||
await cmdRemove(args.force);
|
await cmdDashboard(args.port, args.host);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'list-memories': {
|
case 'init': {
|
||||||
const result = await listMemories({
|
await cmdInit(args.force);
|
||||||
agent_id: args.agent,
|
break;
|
||||||
type: args.type,
|
}
|
||||||
tags: args.tags,
|
|
||||||
pinned: args.pinned,
|
|
||||||
limit: args.limit,
|
|
||||||
offset: args.offset,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!args.full && Array.isArray(result)) {
|
case 'update': {
|
||||||
for (const memory of result) {
|
await cmdUpdate(args.force);
|
||||||
if (typeof memory.content === 'string' && memory.content.length > 200) {
|
break;
|
||||||
memory.content = memory.content.slice(0, 200) + '... (truncated, use --full to see all)';
|
}
|
||||||
}
|
|
||||||
|
case 'remove': {
|
||||||
|
await cmdRemove(args.force);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'list-memories': {
|
||||||
|
const result = await listMemories({
|
||||||
|
agent_id: args.agent,
|
||||||
|
type: args.type,
|
||||||
|
tags: args.tags,
|
||||||
|
pinned: args.pinned,
|
||||||
|
limit: args.limit,
|
||||||
|
offset: args.offset,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!args.full && Array.isArray(result)) {
|
||||||
|
for (const memory of result) {
|
||||||
|
if (typeof memory.content === 'string' && memory.content.length > 200) {
|
||||||
|
memory.content = memory.content.slice(0, 200) + '... (truncated, use --full to see all)';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(JSON.stringify(result, null, 2));
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'inspect-memory': {
|
console.log(JSON.stringify(result, null, 2));
|
||||||
if (!args.id) {
|
break;
|
||||||
console.error('ERROR: --id <memoryId> is required for inspect-memory');
|
}
|
||||||
throw new ExitError(1, 'Missing memory id');
|
|
||||||
}
|
|
||||||
const result = await inspectMemory(args.id);
|
|
||||||
console.log(JSON.stringify(result, null, 2));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'pin-memory': {
|
case 'inspect-memory': {
|
||||||
if (!args.id) {
|
if (!args.id) {
|
||||||
console.error('ERROR: --id <memoryId> is required for pin-memory');
|
console.error('ERROR: --id <memoryId> is required for inspect-memory');
|
||||||
throw new ExitError(1, 'Missing memory id');
|
throw new ExitError(1, 'Missing memory id');
|
||||||
}
|
|
||||||
const result = await pinMemory(args.id);
|
|
||||||
console.log(JSON.stringify(result, null, 2));
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
const result = await inspectMemory(args.id);
|
||||||
|
console.log(JSON.stringify(result, null, 2));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case 'unpin-memory': {
|
case 'pin-memory': {
|
||||||
if (!args.id) {
|
if (!args.id) {
|
||||||
console.error('ERROR: --id <memoryId> is required for unpin-memory');
|
console.error('ERROR: --id <memoryId> is required for pin-memory');
|
||||||
throw new ExitError(1, 'Missing memory id');
|
throw new ExitError(1, 'Missing memory id');
|
||||||
}
|
|
||||||
const result = await unpinMemory(args.id);
|
|
||||||
console.log(JSON.stringify(result, null, 2));
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
const result = await pinMemory(args.id);
|
||||||
|
console.log(JSON.stringify(result, null, 2));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case 'trigger-prune-dry-run': {
|
case 'unpin-memory': {
|
||||||
const result = await triggerPruneDryRun({ retentionMs: args.retentionMs ?? undefined });
|
if (!args.id) {
|
||||||
console.log(JSON.stringify(result, null, 2));
|
console.error('ERROR: --id <memoryId> is required for unpin-memory');
|
||||||
break;
|
throw new ExitError(1, 'Missing memory id');
|
||||||
}
|
}
|
||||||
|
const result = await unpinMemory(args.id);
|
||||||
|
console.log(JSON.stringify(result, null, 2));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case 'memory-stats': {
|
case 'trigger-prune-dry-run': {
|
||||||
const result = await memoryStats({ windowMs: args.windowMs ?? undefined });
|
const result = await triggerPruneDryRun({ retentionMs: args.retentionMs ?? undefined });
|
||||||
console.log(JSON.stringify(result, null, 2));
|
console.log(JSON.stringify(result, null, 2));
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'memory-stats': {
|
||||||
|
const result = await memoryStats({ windowMs: args.windowMs ?? undefined });
|
||||||
|
console.log(JSON.stringify(result, null, 2));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'proposal': {
|
||||||
|
if (!args.subcommand) {
|
||||||
|
console.error('ERROR: Missing subcommand for proposal (create, list, apply, reject, show)');
|
||||||
|
throw new ExitError(1, 'Missing subcommand');
|
||||||
}
|
}
|
||||||
|
await cmdProposal(args.subcommand, {
|
||||||
|
agent: args.agent,
|
||||||
|
target: args.target,
|
||||||
|
contentFile: args.content,
|
||||||
|
reason: args.reason,
|
||||||
|
id: args.id,
|
||||||
|
status: args.status,
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case 'proposal': {
|
case 'rollback': {
|
||||||
if (!args.subcommand) {
|
await cmdRollback(args.target, args.strategy, { list: args.list });
|
||||||
console.error('ERROR: Missing subcommand for proposal (create, list, apply, reject, show)');
|
break;
|
||||||
throw new ExitError(1, 'Missing subcommand');
|
}
|
||||||
}
|
|
||||||
await cmdProposal(args.subcommand, {
|
case 'backup': {
|
||||||
agent: args.agent,
|
const { cmdBackup, listBackups } = await import('./cmd-backup.mjs');
|
||||||
target: args.target,
|
if (args.list) {
|
||||||
contentFile: args.content,
|
const backups = await listBackups();
|
||||||
reason: args.reason,
|
console.log(JSON.stringify(backups, null, 2));
|
||||||
id: args.id,
|
} else {
|
||||||
status: args.status
|
await cmdBackup({ output: args.output });
|
||||||
});
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case 'rollback': {
|
default:
|
||||||
await cmdRollback(args.target, args.strategy, { list: args.list });
|
console.error(`ERROR: Unknown command: ${args.command}`);
|
||||||
break;
|
usage();
|
||||||
}
|
throw new ExitError(1, 'Unknown command');
|
||||||
|
|
||||||
case 'backup': {
|
|
||||||
const { cmdBackup, listBackups } = await import('./cmd-backup.mjs');
|
|
||||||
if (args.list) {
|
|
||||||
const backups = await listBackups();
|
|
||||||
console.log(JSON.stringify(backups, null, 2));
|
|
||||||
} else {
|
|
||||||
await cmdBackup({ output: args.output });
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
console.error(`ERROR: Unknown command: ${args.command}`);
|
|
||||||
usage();
|
|
||||||
throw new ExitError(1, 'Unknown command');
|
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err instanceof ExitError) {
|
if (err instanceof ExitError) {
|
||||||
|
|||||||
+87
-87
@@ -28,19 +28,19 @@ const APP_DIRS = ['src', 'bin', 'dashboard', 'landing', 'assets', 'scripts'];
|
|||||||
const APP_FILES = ['package.json', 'build.mjs', 'README.md', 'torch-config.example.json', 'TORCH.md'];
|
const APP_FILES = ['package.json', 'build.mjs', 'README.md', 'torch-config.example.json', 'TORCH.md'];
|
||||||
|
|
||||||
function getPaths(root, installDirName) {
|
function getPaths(root, installDirName) {
|
||||||
const torchDir = path.resolve(root, installDirName);
|
const torchDir = path.resolve(root, installDirName);
|
||||||
return {
|
return {
|
||||||
root,
|
root,
|
||||||
torchDir,
|
torchDir,
|
||||||
promptsDir: path.join(torchDir, 'prompts'),
|
promptsDir: path.join(torchDir, 'prompts'),
|
||||||
roster: path.join(torchDir, 'roster.json'),
|
roster: path.join(torchDir, 'roster.json'),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function copyDir(src, dest) {
|
function copyDir(src, dest) {
|
||||||
if (fs.existsSync(src)) {
|
if (fs.existsSync(src)) {
|
||||||
fs.cpSync(src, dest, { recursive: true });
|
fs.cpSync(src, dest, { recursive: true });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function transformContent(content, installDirName) {
|
function transformContent(content, installDirName) {
|
||||||
@@ -176,10 +176,10 @@ async function resolveConfiguration(cwd, mockAnswers) {
|
|||||||
|
|
||||||
function ensureInstallDirectory(paths, force, installDir) {
|
function ensureInstallDirectory(paths, force, installDir) {
|
||||||
if (fs.existsSync(paths.torchDir) && !force) {
|
if (fs.existsSync(paths.torchDir) && !force) {
|
||||||
const entries = fs.readdirSync(paths.torchDir);
|
const entries = fs.readdirSync(paths.torchDir);
|
||||||
if (entries.length > 0 && installDir !== '.') {
|
if (entries.length > 0 && installDir !== '.') {
|
||||||
throw new Error(`Directory ${paths.torchDir} already exists and is not empty. Use --force to overwrite.`);
|
throw new Error(`Directory ${paths.torchDir} already exists and is not empty. Use --force to overwrite.`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ensureDir(paths.torchDir);
|
ensureDir(paths.torchDir);
|
||||||
ensureDir(paths.promptsDir);
|
ensureDir(paths.promptsDir);
|
||||||
@@ -249,17 +249,17 @@ function configureTorch(cwd, paths, installDir, namespace, relays, hashtag) {
|
|||||||
|
|
||||||
// Try to load existing or example
|
// Try to load existing or example
|
||||||
if (fs.existsSync(configPath)) {
|
if (fs.existsSync(configPath)) {
|
||||||
try {
|
try {
|
||||||
configData = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
configData = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
||||||
console.log(`Updating existing ${path.relative(cwd, configPath)}...`);
|
console.log(`Updating existing ${path.relative(cwd, configPath)}...`);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn(`Could not parse existing config: ${e.message}`);
|
console.warn(`Could not parse existing config: ${e.message}`);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const exampleConfigPath = path.join(PKG_ROOT, 'torch-config.example.json');
|
const exampleConfigPath = path.join(PKG_ROOT, 'torch-config.example.json');
|
||||||
if (fs.existsSync(exampleConfigPath)) {
|
if (fs.existsSync(exampleConfigPath)) {
|
||||||
configData = JSON.parse(fs.readFileSync(exampleConfigPath, 'utf8'));
|
configData = JSON.parse(fs.readFileSync(exampleConfigPath, 'utf8'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply user choices
|
// Apply user choices
|
||||||
@@ -286,11 +286,11 @@ function configureTorch(cwd, paths, installDir, namespace, relays, hashtag) {
|
|||||||
['daily', 'weekly'].forEach(cadence => {
|
['daily', 'weekly'].forEach(cadence => {
|
||||||
if (!configData.scheduler.memoryPolicyByCadence[cadence]) {
|
if (!configData.scheduler.memoryPolicyByCadence[cadence]) {
|
||||||
configData.scheduler.memoryPolicyByCadence[cadence] = {
|
configData.scheduler.memoryPolicyByCadence[cadence] = {
|
||||||
mode: "required",
|
mode: 'required',
|
||||||
retrieveSuccessMarkers: ["MEMORY_RETRIEVED"],
|
retrieveSuccessMarkers: ['MEMORY_RETRIEVED'],
|
||||||
storeSuccessMarkers: ["MEMORY_STORED"],
|
storeSuccessMarkers: ['MEMORY_STORED'],
|
||||||
retrieveArtifacts: [`.scheduler-memory/latest/${cadence}/retrieve.ok`],
|
retrieveArtifacts: [`.scheduler-memory/latest/${cadence}/retrieve.ok`],
|
||||||
storeArtifacts: [`.scheduler-memory/latest/${cadence}/store.ok`]
|
storeArtifacts: [`.scheduler-memory/latest/${cadence}/store.ok`],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -308,7 +308,7 @@ function injectHostScriptsIfNeeded(paths, installDir) {
|
|||||||
// 7. Inject Scripts into Host Package.json
|
// 7. Inject Scripts into Host Package.json
|
||||||
// If we are NOT installing to '.', the host package.json is in paths.root
|
// If we are NOT installing to '.', the host package.json is in paths.root
|
||||||
if (installDir !== '.') {
|
if (installDir !== '.') {
|
||||||
injectScriptsIntoHost(paths.root, installDir);
|
injectScriptsIntoHost(paths.root, installDir);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -361,44 +361,44 @@ To change these settings, edit \`torch-config.json\` in your project root.
|
|||||||
}
|
}
|
||||||
|
|
||||||
function injectScriptsIntoHost(hostRoot, installDirName) {
|
function injectScriptsIntoHost(hostRoot, installDirName) {
|
||||||
const hostPkgPath = path.join(hostRoot, 'package.json');
|
const hostPkgPath = path.join(hostRoot, 'package.json');
|
||||||
if (!fs.existsSync(hostPkgPath)) {
|
if (!fs.existsSync(hostPkgPath)) {
|
||||||
console.warn('No package.json found in host root. Skipping script injection.');
|
console.warn('No package.json found in host root. Skipping script injection.');
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const pkg = JSON.parse(fs.readFileSync(hostPkgPath, 'utf8'));
|
||||||
|
if (!pkg.scripts) pkg.scripts = {};
|
||||||
|
|
||||||
|
const scriptsToAdd = {
|
||||||
|
'torch:dashboard': `npm run --prefix ${installDirName} dashboard:serve`,
|
||||||
|
'torch:check': `npm run --prefix ${installDirName} lock:check:daily`, // Default to daily check
|
||||||
|
'torch:lock': `npm run --prefix ${installDirName} lock:lock`,
|
||||||
|
'torch:health': `npm run --prefix ${installDirName} lock:health`,
|
||||||
|
'torch:memory:list': `node ${installDirName === '.' ? '' : installDirName + '/'}bin/torch-lock.mjs list-memories`,
|
||||||
|
'torch:memory:inspect': `node ${installDirName === '.' ? '' : installDirName + '/'}bin/torch-lock.mjs inspect-memory`,
|
||||||
|
};
|
||||||
|
|
||||||
|
let modified = false;
|
||||||
|
for (const [key, cmd] of Object.entries(scriptsToAdd)) {
|
||||||
|
if (!pkg.scripts[key]) {
|
||||||
|
pkg.scripts[key] = cmd;
|
||||||
|
console.log(` Added script: "${key}"`);
|
||||||
|
modified = true;
|
||||||
|
} else {
|
||||||
|
console.log(` Script "${key}" already exists, skipping.`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
if (modified) {
|
||||||
const pkg = JSON.parse(fs.readFileSync(hostPkgPath, 'utf8'));
|
fs.writeFileSync(hostPkgPath, JSON.stringify(pkg, null, 2), 'utf8');
|
||||||
if (!pkg.scripts) pkg.scripts = {};
|
console.log('Updated package.json with convenience scripts.');
|
||||||
|
|
||||||
const scriptsToAdd = {
|
|
||||||
'torch:dashboard': `npm run --prefix ${installDirName} dashboard:serve`,
|
|
||||||
'torch:check': `npm run --prefix ${installDirName} lock:check:daily`, // Default to daily check
|
|
||||||
'torch:lock': `npm run --prefix ${installDirName} lock:lock`,
|
|
||||||
'torch:health': `npm run --prefix ${installDirName} lock:health`,
|
|
||||||
'torch:memory:list': `node ${installDirName === '.' ? '' : installDirName + '/'}bin/torch-lock.mjs list-memories`,
|
|
||||||
'torch:memory:inspect': `node ${installDirName === '.' ? '' : installDirName + '/'}bin/torch-lock.mjs inspect-memory`,
|
|
||||||
};
|
|
||||||
|
|
||||||
let modified = false;
|
|
||||||
for (const [key, cmd] of Object.entries(scriptsToAdd)) {
|
|
||||||
if (!pkg.scripts[key]) {
|
|
||||||
pkg.scripts[key] = cmd;
|
|
||||||
console.log(` Added script: "${key}"`);
|
|
||||||
modified = true;
|
|
||||||
} else {
|
|
||||||
console.log(` Script "${key}" already exists, skipping.`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (modified) {
|
|
||||||
fs.writeFileSync(hostPkgPath, JSON.stringify(pkg, null, 2), 'utf8');
|
|
||||||
console.log('Updated package.json with convenience scripts.');
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (e) {
|
|
||||||
console.error(`Failed to inject scripts: ${e.message}`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
console.error(`Failed to inject scripts: ${e.message}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -431,7 +431,7 @@ function detectTorchInstallDir(cwd) {
|
|||||||
if (pkg.name === 'torch-lock') {
|
if (pkg.name === 'torch-lock') {
|
||||||
return '.';
|
return '.';
|
||||||
}
|
}
|
||||||
} catch (_e) {
|
} catch {
|
||||||
// Ignore parse errors
|
// Ignore parse errors
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -571,7 +571,7 @@ export async function cmdRemove(force = false, cwd = process.cwd(), mockAnswers
|
|||||||
if (pkg.scripts) {
|
if (pkg.scripts) {
|
||||||
hasHostScripts = TORCH_HOST_SCRIPTS.some(key => key in pkg.scripts);
|
hasHostScripts = TORCH_HOST_SCRIPTS.some(key => key in pkg.scripts);
|
||||||
}
|
}
|
||||||
} catch (_e) { /* ignore */ }
|
} catch { /* ignore */ }
|
||||||
}
|
}
|
||||||
|
|
||||||
if (targets.length === 0 && !hasHostScripts) {
|
if (targets.length === 0 && !hasHostScripts) {
|
||||||
@@ -628,7 +628,7 @@ export async function cmdRemove(force = false, cwd = process.cwd(), mockAnswers
|
|||||||
fs.rmdirSync(srcDir);
|
fs.rmdirSync(srcDir);
|
||||||
log(' Removed empty src/ directory');
|
log(' Removed empty src/ directory');
|
||||||
}
|
}
|
||||||
} catch (_e) { /* leave it if not empty or permission error */ }
|
} catch { /* leave it if not empty or permission error */ }
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Remove host package.json scripts
|
// 3. Remove host package.json scripts
|
||||||
@@ -664,15 +664,15 @@ export function cmdUpdate(force = false, cwd = process.cwd()) {
|
|||||||
|
|
||||||
let installDirName = 'torch';
|
let installDirName = 'torch';
|
||||||
if (!fs.existsSync(path.join(cwd, 'torch')) && fs.existsSync(path.join(cwd, 'package.json'))) {
|
if (!fs.existsSync(path.join(cwd, 'torch')) && fs.existsSync(path.join(cwd, 'package.json'))) {
|
||||||
// Check if current dir is the torch dir
|
// Check if current dir is the torch dir
|
||||||
try {
|
try {
|
||||||
const pkg = JSON.parse(fs.readFileSync(path.join(cwd, 'package.json'), 'utf8'));
|
const pkg = JSON.parse(fs.readFileSync(path.join(cwd, 'package.json'), 'utf8'));
|
||||||
if (pkg.name === 'torch-lock') {
|
if (pkg.name === 'torch-lock') {
|
||||||
installDirName = '.';
|
installDirName = '.';
|
||||||
}
|
|
||||||
} catch (_e) {
|
|
||||||
// Ignore error if package.json is missing or invalid
|
|
||||||
}
|
}
|
||||||
|
} catch {
|
||||||
|
// Ignore error if package.json is missing or invalid
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const paths = getPaths(cwd, installDirName);
|
const paths = getPaths(cwd, installDirName);
|
||||||
@@ -693,11 +693,11 @@ export function cmdUpdate(force = false, cwd = process.cwd()) {
|
|||||||
// We backup EVERYTHING in torchDir except _backups and node_modules
|
// We backup EVERYTHING in torchDir except _backups and node_modules
|
||||||
const entries = fs.readdirSync(paths.torchDir);
|
const entries = fs.readdirSync(paths.torchDir);
|
||||||
for (const entry of entries) {
|
for (const entry of entries) {
|
||||||
if (entry === '_backups' || entry === 'node_modules' || entry === '.git') continue;
|
if (entry === '_backups' || entry === 'node_modules' || entry === '.git') continue;
|
||||||
|
|
||||||
const srcPath = path.join(paths.torchDir, entry);
|
const srcPath = path.join(paths.torchDir, entry);
|
||||||
const destPath = path.join(thisBackupDir, entry);
|
const destPath = path.join(thisBackupDir, entry);
|
||||||
fs.cpSync(srcPath, destPath, { recursive: true });
|
fs.cpSync(srcPath, destPath, { recursive: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Update App Directories (Overwrite)
|
// 2. Update App Directories (Overwrite)
|
||||||
@@ -746,15 +746,15 @@ export function cmdUpdate(force = false, cwd = process.cwd()) {
|
|||||||
const destFile = path.join(destDir, file);
|
const destFile = path.join(destDir, file);
|
||||||
|
|
||||||
if (force) {
|
if (force) {
|
||||||
copyFile(srcFile, destFile, false, true, installDirName);
|
copyFile(srcFile, destFile, false, true, installDirName);
|
||||||
updated++;
|
updated++;
|
||||||
} else {
|
} else {
|
||||||
if (!fs.existsSync(destFile)) {
|
if (!fs.existsSync(destFile)) {
|
||||||
copyFile(srcFile, destFile, false, true, installDirName);
|
copyFile(srcFile, destFile, false, true, installDirName);
|
||||||
added++;
|
added++;
|
||||||
} else {
|
} else {
|
||||||
skipped++;
|
skipped++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
console.log(` ${dir}/: ${added} added, ${updated} updated, ${skipped} preserved`);
|
console.log(` ${dir}/: ${added} added, ${updated} updated, ${skipped} preserved`);
|
||||||
|
|||||||
@@ -9,9 +9,9 @@ const originalConfig = fs.existsSync(configPath) ? fs.readFileSync(configPath) :
|
|||||||
if (!fs.existsSync(configPath)) {
|
if (!fs.existsSync(configPath)) {
|
||||||
fs.writeFileSync(configPath, JSON.stringify({
|
fs.writeFileSync(configPath, JSON.stringify({
|
||||||
nostrLock: {
|
nostrLock: {
|
||||||
relays: ["wss://relay.damus.io"],
|
relays: ['wss://relay.damus.io'],
|
||||||
namespace: "test-ns"
|
namespace: 'test-ns',
|
||||||
}
|
},
|
||||||
}, null, 2));
|
}, null, 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -19,27 +19,27 @@ const ITERATIONS = 1000;
|
|||||||
const start = process.hrtime.bigint();
|
const start = process.hrtime.bigint();
|
||||||
|
|
||||||
async function run() {
|
async function run() {
|
||||||
for (let i = 0; i < ITERATIONS; i++) {
|
for (let i = 0; i < ITERATIONS; i++) {
|
||||||
_resetTorchConfigCache();
|
_resetTorchConfigCache();
|
||||||
try {
|
try {
|
||||||
const res = await loadTorchConfig();
|
const res = await loadTorchConfig();
|
||||||
// Use res to prevent dead code elimination if JIT is smart (though side effects exist)
|
// Use res to prevent dead code elimination if JIT is smart (though side effects exist)
|
||||||
if (!res) throw new Error("Failed");
|
if (!res) throw new Error('Failed');
|
||||||
} catch (_e) {
|
} catch {
|
||||||
// ignore
|
// ignore
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const end = process.hrtime.bigint();
|
const end = process.hrtime.bigint();
|
||||||
const duration = Number(end - start) / 1e6; // ms
|
const duration = Number(end - start) / 1e6; // ms
|
||||||
|
|
||||||
console.log(`[ASYNC] Loaded config ${ITERATIONS} times in ${duration.toFixed(2)}ms`);
|
console.log(`[ASYNC] Loaded config ${ITERATIONS} times in ${duration.toFixed(2)}ms`);
|
||||||
console.log(`[ASYNC] Average: ${(duration / ITERATIONS).toFixed(4)}ms per load`);
|
console.log(`[ASYNC] Average: ${(duration / ITERATIONS).toFixed(4)}ms per load`);
|
||||||
|
|
||||||
// Cleanup
|
// Cleanup
|
||||||
if (!originalConfig && fs.existsSync(configPath)) {
|
if (!originalConfig && fs.existsSync(configPath)) {
|
||||||
fs.unlinkSync(configPath);
|
fs.unlinkSync(configPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
run();
|
run();
|
||||||
@@ -28,9 +28,9 @@ export async function listProposals() {
|
|||||||
const meta = JSON.parse(metaContent);
|
const meta = JSON.parse(metaContent);
|
||||||
proposals.push({
|
proposals.push({
|
||||||
id: entry.name,
|
id: entry.name,
|
||||||
...meta
|
...meta,
|
||||||
});
|
});
|
||||||
} catch (_e) {
|
} catch {
|
||||||
// Ignore invalid proposals
|
// Ignore invalid proposals
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -59,7 +59,7 @@ export async function getProposal(id) {
|
|||||||
meta,
|
meta,
|
||||||
newContent,
|
newContent,
|
||||||
diff,
|
diff,
|
||||||
dir
|
dir,
|
||||||
};
|
};
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw new Error(`Proposal ${id} not found or invalid: ${e.message}`, { cause: e });
|
throw new Error(`Proposal ${id} not found or invalid: ${e.message}`, { cause: e });
|
||||||
@@ -103,11 +103,11 @@ export async function createProposal({ agent, target, newContent, reason }) {
|
|||||||
if (e.status === 1 && e.stdout) {
|
if (e.status === 1 && e.stdout) {
|
||||||
diff = e.stdout.toString();
|
diff = e.stdout.toString();
|
||||||
} else if (e.status !== 1) {
|
} else if (e.status !== 1) {
|
||||||
// Some other error
|
// Some other error
|
||||||
console.error('Diff generation failed:', e.message);
|
console.error('Diff generation failed:', e.message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (_err) {
|
} catch {
|
||||||
diff = '(New File)';
|
diff = '(New File)';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,7 +119,7 @@ export async function createProposal({ agent, target, newContent, reason }) {
|
|||||||
target,
|
target,
|
||||||
reason,
|
reason,
|
||||||
timestamp: new Date().toISOString(),
|
timestamp: new Date().toISOString(),
|
||||||
status: 'pending'
|
status: 'pending',
|
||||||
};
|
};
|
||||||
|
|
||||||
await fs.writeFile(path.join(dir, 'meta.json'), JSON.stringify(meta, null, 2));
|
await fs.writeFile(path.join(dir, 'meta.json'), JSON.stringify(meta, null, 2));
|
||||||
@@ -146,7 +146,7 @@ export async function validateProposal(id) {
|
|||||||
|
|
||||||
for (const pattern of requiredPatterns) {
|
for (const pattern of requiredPatterns) {
|
||||||
if (!pattern.test(newContent)) {
|
if (!pattern.test(newContent)) {
|
||||||
return { valid: false, reason: `Missing required header pattern: ${pattern}` };
|
return { valid: false, reason: `Missing required header pattern: ${pattern}` };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -195,7 +195,7 @@ export async function applyProposal(id) {
|
|||||||
archivedAt: new Date().toISOString(),
|
archivedAt: new Date().toISOString(),
|
||||||
};
|
};
|
||||||
await fs.writeFile(archivePath.replace(/\.md$/, '.meta.json'), JSON.stringify(archiveMeta, null, 2));
|
await fs.writeFile(archivePath.replace(/\.md$/, '.meta.json'), JSON.stringify(archiveMeta, null, 2));
|
||||||
} catch (_e) {
|
} catch {
|
||||||
// If file didn't exist, nothing to archive
|
// If file didn't exist, nothing to archive
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -272,7 +272,7 @@ export async function listPromptVersions(target) {
|
|||||||
// Convert back: YYYY-MM-DDTHH-MM-SS-mmmZ -> ISO
|
// Convert back: YYYY-MM-DDTHH-MM-SS-mmmZ -> ISO
|
||||||
archivedAt = tsPart.replace(
|
archivedAt = tsPart.replace(
|
||||||
/^(\d{4}-\d{2}-\d{2}T\d{2})-(\d{2})-(\d{2})-(\d{3})Z$/,
|
/^(\d{4}-\d{2}-\d{2}T\d{2})-(\d{2})-(\d{2})-(\d{3})Z$/,
|
||||||
'$1:$2:$3.$4Z'
|
'$1:$2:$3.$4Z',
|
||||||
);
|
);
|
||||||
if (archivedAt === tsPart) archivedAt = null; // parse failed
|
if (archivedAt === tsPart) archivedAt = null; // parse failed
|
||||||
} else {
|
} else {
|
||||||
@@ -295,7 +295,7 @@ export async function listPromptVersions(target) {
|
|||||||
reason: meta?.reason ?? null,
|
reason: meta?.reason ?? null,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (_e) {
|
} catch {
|
||||||
// archiveDir doesn't exist — no versions yet
|
// archiveDir doesn't exist — no versions yet
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -332,7 +332,7 @@ export async function rollbackPrompt(target, hashOrStrategy = 'latest') {
|
|||||||
sourceContent = await fs.readFile(path.join(archiveDir, match), 'utf8');
|
sourceContent = await fs.readFile(path.join(archiveDir, match), 'utf8');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (_e) {
|
} catch {
|
||||||
// Archive lookup failed
|
// Archive lookup failed
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ function toContextString(agentContext) {
|
|||||||
return agentContext.trim();
|
return agentContext.trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (agentContext == null) {
|
if (agentContext === null || agentContext === undefined) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -197,7 +197,7 @@ export async function createLifecyclePlan(memories, options) {
|
|||||||
|
|
||||||
const templates = loadMemoryPromptTemplates({ templateDir: options.templateDir });
|
const templates = loadMemoryPromptTemplates({ templateDir: options.templateDir });
|
||||||
const condensedGroups = await Promise.all(
|
const condensedGroups = await Promise.all(
|
||||||
groups.map((group) => condenseGroup(group, options.generateSummary, templates.condense))
|
groups.map((group) => condenseGroup(group, options.generateSummary, templates.condense)),
|
||||||
);
|
);
|
||||||
|
|
||||||
const mergedIntoMap = new Map();
|
const mergedIntoMap = new Map();
|
||||||
|
|||||||
@@ -54,8 +54,8 @@ function textMatchSimilarity(queryTerms, queryRegexes, memory) {
|
|||||||
*/
|
*/
|
||||||
function matchesMetadataFilters(memory, params) {
|
function matchesMetadataFilters(memory, params) {
|
||||||
const inTimeframe = !params.timeframe
|
const inTimeframe = !params.timeframe
|
||||||
|| ((params.timeframe.from == null || memory.created_at >= params.timeframe.from)
|
|| ((params.timeframe.from === null || memory.created_at >= params.timeframe.from)
|
||||||
&& (params.timeframe.to == null || memory.created_at <= params.timeframe.to));
|
&& (params.timeframe.to === null || memory.created_at <= params.timeframe.to));
|
||||||
|
|
||||||
if (!inTimeframe) return false;
|
if (!inTimeframe) return false;
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ function redactObviousPii(text) {
|
|||||||
function renderTemplate(template, replacements) {
|
function renderTemplate(template, replacements) {
|
||||||
return Object.entries(replacements).reduce(
|
return Object.entries(replacements).reduce(
|
||||||
(output, [key, value]) => output.replaceAll(`{{${key}}}`, String(value ?? '')),
|
(output, [key, value]) => output.replaceAll(`{{${key}}}`, String(value ?? '')),
|
||||||
template
|
template,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,7 +40,7 @@ export function loadMemoryPromptTemplates(options = {}) {
|
|||||||
const filePath = path.join(templateDir, fileName);
|
const filePath = path.join(templateDir, fileName);
|
||||||
const content = readFileSync(filePath, 'utf8').trim();
|
const content = readFileSync(filePath, 'utf8').trim();
|
||||||
return [key, content];
|
return [key, content];
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
templateCache.set(templateDir, templates);
|
templateCache.set(templateDir, templates);
|
||||||
@@ -77,7 +77,7 @@ function isLikelyFactual(summary, sourceText) {
|
|||||||
sourceText
|
sourceText
|
||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
.split(/[^a-z0-9]+/)
|
.split(/[^a-z0-9]+/)
|
||||||
.filter((token) => token.length > 3)
|
.filter((token) => token.length > 3),
|
||||||
);
|
);
|
||||||
const summaryTokens = summary
|
const summaryTokens = summary
|
||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
import { describe, it } from 'node:test';
|
||||||
|
import assert from 'node:assert';
|
||||||
|
import { estimateTokenCount } from '../src/services/memory/formatter.js';
|
||||||
|
|
||||||
|
describe('formatter', () => {
|
||||||
|
describe('estimateTokenCount', () => {
|
||||||
|
it('should return 0 for empty string', () => {
|
||||||
|
const result = estimateTokenCount('');
|
||||||
|
assert.strictEqual(result, 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return 0 for null', () => {
|
||||||
|
const result = estimateTokenCount(null);
|
||||||
|
assert.strictEqual(result, 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should estimate tokens at approximately 4 chars per token', () => {
|
||||||
|
const text = 'This is a test string with some content';
|
||||||
|
const result = estimateTokenCount(text);
|
||||||
|
// 40 chars / 4 = 10 tokens (approximately)
|
||||||
|
assert(result > 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle long strings', () => {
|
||||||
|
const text = 'a'.repeat(1000);
|
||||||
|
const result = estimateTokenCount(text);
|
||||||
|
assert.strictEqual(result, 250);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
import { describe, it } from 'node:test';
|
||||||
|
import assert from 'node:assert';
|
||||||
|
import { parseLockEvent } from '../src/lock-ops.mjs';
|
||||||
|
|
||||||
|
describe('parseLockEvent', () => {
|
||||||
|
it('should parse a valid lock event', () => {
|
||||||
|
const event = {
|
||||||
|
id: 'test-event-id',
|
||||||
|
pubkey: 'test-pubkey',
|
||||||
|
created_at: Math.floor(Date.now() / 1000),
|
||||||
|
kind: 30078,
|
||||||
|
tags: [
|
||||||
|
['d', 'torch-lock/'],
|
||||||
|
['t', 'torch-agent-lock'],
|
||||||
|
],
|
||||||
|
content: JSON.stringify({
|
||||||
|
agent: 'test-agent',
|
||||||
|
cadence: 'daily',
|
||||||
|
date: '2026-02-20',
|
||||||
|
git_commit: 'abc123',
|
||||||
|
prompt_hash: 'def456',
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = parseLockEvent(event);
|
||||||
|
|
||||||
|
assert.strictEqual(result.agent, 'test-agent');
|
||||||
|
assert.strictEqual(result.cadence, 'daily');
|
||||||
|
assert.strictEqual(result.date, '2026-02-20');
|
||||||
|
assert.strictEqual(result.eventId, 'test-event-id');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle event with missing content fields', () => {
|
||||||
|
const event = {
|
||||||
|
id: 'test-event-id',
|
||||||
|
pubkey: 'test-pubkey',
|
||||||
|
created_at: Math.floor(Date.now() / 1000),
|
||||||
|
kind: 30078,
|
||||||
|
tags: [['d', 'torch-lock/']],
|
||||||
|
content: '{}',
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = parseLockEvent(event);
|
||||||
|
|
||||||
|
assert.strictEqual(result.agent, null);
|
||||||
|
assert.strictEqual(result.cadence, null);
|
||||||
|
assert.strictEqual(result.date, null);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should parse event with expiration tag', () => {
|
||||||
|
const event = {
|
||||||
|
id: 'test-event-id',
|
||||||
|
pubkey: 'test-pubkey',
|
||||||
|
created_at: Math.floor(Date.now() / 1000),
|
||||||
|
kind: 30078,
|
||||||
|
tags: [
|
||||||
|
['d', 'torch-lock/'],
|
||||||
|
['expiration', '1771603837'],
|
||||||
|
],
|
||||||
|
content: '{}',
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = parseLockEvent(event);
|
||||||
|
|
||||||
|
assert.strictEqual(result.expiresAt, 1771603837);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle malformed JSON content gracefully', () => {
|
||||||
|
const event = {
|
||||||
|
id: 'test-event-id',
|
||||||
|
pubkey: 'test-pubkey',
|
||||||
|
created_at: Math.floor(Date.now() / 1000),
|
||||||
|
kind: 30078,
|
||||||
|
tags: [['d', 'torch-lock/']],
|
||||||
|
content: 'not valid json',
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = parseLockEvent(event);
|
||||||
|
|
||||||
|
assert.strictEqual(result.agent, null);
|
||||||
|
assert.strictEqual(result.cadence, null);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
import { describe, it } from 'node:test';
|
||||||
|
import assert from 'node:assert';
|
||||||
|
import { todayDateStr, detectPlatform } from '../src/utils.mjs';
|
||||||
|
|
||||||
|
describe('utils', () => {
|
||||||
|
describe('todayDateStr', () => {
|
||||||
|
it('should return date string in YYYY-MM-DD format', () => {
|
||||||
|
const result = todayDateStr();
|
||||||
|
assert.match(result, /^\d{4}-\d{2}-\d{2}$/);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('detectPlatform', () => {
|
||||||
|
it('should return null when no platform env vars are set', () => {
|
||||||
|
const result = detectPlatform();
|
||||||
|
// Should return a string or null depending on environment
|
||||||
|
assert(result === null || typeof result === 'string');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should detect qwen platform from environment', () => {
|
||||||
|
const original = process.env.QWEN_API_KEY;
|
||||||
|
try {
|
||||||
|
process.env.QWEN_API_KEY = 'test-key';
|
||||||
|
const result = detectPlatform();
|
||||||
|
assert.strictEqual(result, 'qwen');
|
||||||
|
} finally {
|
||||||
|
if (original) {
|
||||||
|
process.env.QWEN_API_KEY = original;
|
||||||
|
} else {
|
||||||
|
delete process.env.QWEN_API_KEY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in new issue
Block a user