This commit is contained in:
thePR0M3TH3AN committed 2026-02-20 10:26:39 -05:00
1 parent cc1ba691cb
commit deadeaafac
73 files changed
+3085 -699

No files matched your search

+1
View File
@@ -0,0 +1 @@
torch/node_modules
+1
View File
@@ -0,0 +1 @@
MEMORY_STORED
+376 -1
View File
@@ -17,7 +17,7 @@
"importance": 0.4,
"embedding_id": null,
"created_at": 1771558399336,
"last_seen": 1771558399343,
"last_seen": 1771596755883,
"source": "scheduler-retrieve",
"ttl_days": null,
"merged_into": null,
@@ -48,5 +48,380 @@
"merged_into": null,
"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.*
+55
View File
@@ -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/**',
],
},
];
+1036
View File
File diff suppressed because it is too large. Load diff
+3 -3
View File
@@ -46,9 +46,9 @@ const LOG_FILE = 'artifacts/debug_frontend.log';
});
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 });
log(`Navigation complete.`);
log('Navigation complete.');
// Give it a bit more time for any delayed errors
await page.waitForTimeout(2000);
} catch (e) {
@@ -64,7 +64,7 @@ const LOG_FILE = 'artifacts/debug_frontend.log';
console.error(`\nFound ${errors.length} errors.`);
process.exit(1);
} else {
console.log(`\nNo errors found.`);
console.log('\nNo errors found.');
process.exit(0);
}
})();
+1 -1
View File
@@ -91,7 +91,7 @@ function generateEvent() {
pubkey: randomString(64), // Mock Pubkey
created_at: Math.floor(Date.now() / 1000),
tags: tags,
content: content
content: content,
};
}
+31 -31
View File
@@ -40,7 +40,7 @@ const stats = {
latencyMax: 0,
errors: {},
startTime: Date.now(),
endTime: 0
endTime: 0,
};
async function sleep(ms) {
@@ -103,35 +103,35 @@ async function main() {
return new Promise((resolve) => {
const timer = setInterval(async () => {
if (Date.now() >= end) {
clearInterval(timer);
pool.close([RELAY_URL]);
stats.endTime = Date.now();
writeReport(reportDir);
resolve();
return;
}
if (Date.now() >= end) {
clearInterval(timer);
pool.close([RELAY_URL]);
stats.endTime = Date.now();
writeReport(reportDir);
resolve();
return;
}
const clientIdx = Math.floor(Math.random() * CLIENTS);
const sk = sks[clientIdx];
const event = generateEvent(sk);
const clientIdx = Math.floor(Math.random() * CLIENTS);
const sk = sks[clientIdx];
const event = generateEvent(sk);
stats.totalAttempts++;
const sendStart = Date.now();
stats.totalAttempts++;
const sendStart = Date.now();
try {
await Promise.any(pool.publish([RELAY_URL], event));
const latency = Date.now() - sendStart;
stats.success++;
stats.latencySum += latency;
stats.latencyMin = Math.min(stats.latencyMin, latency);
stats.latencyMax = Math.max(stats.latencyMax, latency);
} catch (err) {
stats.failed++;
const msg = err.message || String(err);
stats.errors[msg] = (stats.errors[msg] || 0) + 1;
}
}, interval);
try {
await Promise.any(pool.publish([RELAY_URL], event));
const latency = Date.now() - sendStart;
stats.success++;
stats.latencySum += latency;
stats.latencyMin = Math.min(stats.latencyMin, latency);
stats.latencyMax = Math.max(stats.latencyMax, latency);
} catch (err) {
stats.failed++;
const msg = err.message || String(err);
stats.errors[msg] = (stats.errors[msg] || 0) + 1;
}
}, interval);
});
}
@@ -147,7 +147,7 @@ function writeReport(dir) {
durationSec: DURATION_SEC,
rateEps: RATE_EPS,
mix: MIX,
dryRun: DRY_RUN
dryRun: DRY_RUN,
},
results: {
totalAttempts: stats.totalAttempts,
@@ -157,11 +157,11 @@ function writeReport(dir) {
latency: {
min: stats.latencyMin === Infinity ? 0 : stats.latencyMin,
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);
+4 -4
View File
@@ -435,10 +435,10 @@ async function main() {
const excluded = Array.isArray(checkPayload.excluded)
? checkPayload.excluded
: [...new Set([
...(Array.isArray(checkPayload.locked) ? checkPayload.locked : []),
...(Array.isArray(checkPayload.paused) ? checkPayload.paused : []),
...(Array.isArray(checkPayload.completed) ? checkPayload.completed : []),
])];
...(Array.isArray(checkPayload.locked) ? checkPayload.locked : []),
...(Array.isArray(checkPayload.paused) ? checkPayload.paused : []),
...(Array.isArray(checkPayload.completed) ? checkPayload.completed : []),
])];
const excludedSet = new Set(excluded);
const latestFile = await getLatestFile(logDir);
+1 -1
View File
@@ -26,7 +26,7 @@ function resolveRunner(promptMarkdown) {
}
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.`);
+3 -3
View File
@@ -42,7 +42,7 @@ async function waitForServer(url, timeoutMs = 10000) {
try {
const res = await fetch(url);
if (res.ok) return true;
} catch (_e) {
} catch {
// ignore
}
await sleep(500);
@@ -63,7 +63,7 @@ async function main() {
const results = {
timestamp: new Date().toISOString(),
steps: [],
success: false
success: false,
};
let serverProcess = null;
@@ -133,7 +133,7 @@ async function main() {
},
oneose() {
sub.close();
}
},
});
// Wait for event
+2 -2
View File
@@ -109,7 +109,7 @@ async function listRecentFiles(dirPath, sinceMs) {
if (!entry.isFile()) continue;
const filePath = path.join(dirPath, entry.name);
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 });
}
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 activeRunStart = args.runStart || null;
if (args.since && sinceMs == null) {
if (args.since && sinceMs === null) {
console.error(`Invalid --since value: ${args.since}`);
process.exit(2);
}
+11 -11
View File
@@ -22,7 +22,7 @@ async function waitForServer() {
req.end();
});
return;
} catch (_e) {
} catch {
await sleep(100);
}
}
@@ -43,7 +43,7 @@ function makeRequest() {
async function benchmark() {
console.log('Starting server...');
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 {
@@ -54,20 +54,20 @@ async function benchmark() {
let sent = 0;
const worker = async () => {
while (true) {
const current = sent++;
if (current >= REQUESTS) break;
try {
await makeRequest();
} catch (_e) {
// console.error(_e);
}
while (true) {
const current = sent++;
if (current >= REQUESTS) break;
try {
await makeRequest();
} catch {
// console.error(_e);
}
}
};
const workers = [];
for (let i = 0; i < CONCURRENCY; i++) {
workers.push(worker());
workers.push(worker());
}
await Promise.all(workers);
+1 -1
View File
@@ -121,7 +121,7 @@ async function runBenchmark() {
const condensedCount = plan.condensedGroups.length;
// We expect exactly GROUPS condensed 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);
+1 -1
View File
@@ -74,7 +74,7 @@ Options:
continue;
}
if (['.js', '.mjs', '.ts', '.html', '.css', '.json', '.md'].includes(path.extname(item))) {
files.push(fullPath);
files.push(fullPath);
}
}
}
+2 -2
View File
@@ -69,14 +69,14 @@ Options:
if (stat.isDirectory()) {
if (!EXCLUDED_DIRS.includes(item)) {
files = files.concat(scanDirectory(fullPath));
files = files.concat(scanDirectory(fullPath));
}
} else if (stat.isFile()) {
if (EXCLUDED_FILES.includes(item)) {
continue;
}
if (TARGET_EXTENSIONS.includes(path.extname(item))) {
files.push(fullPath);
files.push(fullPath);
}
}
}
+58 -58
View File
@@ -13,20 +13,20 @@ const runId = process.env.SCHEDULER_RUN_ID ||
`session-${Date.now().toString(36)}`;
// Extract prompt intent
let promptIntent = `scheduler memory retrieval`;
let promptIntent = 'scheduler memory retrieval';
if (promptPath) {
try {
const promptRaw = readFileSync(promptPath, 'utf8');
const promptLines = promptRaw.split(/\r?\n/).map(line => line.trim()).filter(Boolean);
const intentLine = promptLines.find(line => line.startsWith('#') || line.toLowerCase().startsWith('goal'));
if (intentLine) {
promptIntent = intentLine.replace(/^#+\s*/, '');
} else if (promptLines.length > 0) {
promptIntent = promptLines[0];
}
} catch (err) {
console.warn(`Could not read prompt file: ${promptPath}`, err.message);
try {
const promptRaw = readFileSync(promptPath, 'utf8');
const promptLines = promptRaw.split(/\r?\n/).map(line => line.trim()).filter(Boolean);
const intentLine = promptLines.find(line => line.startsWith('#') || line.toLowerCase().startsWith('goal'));
if (intentLine) {
promptIntent = intentLine.replace(/^#+\s*/, '');
} else if (promptLines.length > 0) {
promptIntent = promptLines[0];
}
} catch (err) {
console.warn(`Could not read prompt file: ${promptPath}`, err.message);
}
}
// Construct query
@@ -35,60 +35,60 @@ const timestamp = Date.now();
// Create retrieval seed event
const events = [{
agent_id: agentId,
content: `Memory retrieval seed for ${cadence} :: ${promptIntent}`,
timestamp,
tags: ['scheduler', cadence, 'retrieve'],
metadata: {
session_id: runId,
source: 'scheduler-retrieve',
importance: 0.4,
prompt_path: promptPath
}
agent_id: agentId,
content: `Memory retrieval seed for ${cadence} :: ${promptIntent}`,
timestamp,
tags: ['scheduler', cadence, 'retrieve'],
metadata: {
session_id: runId,
source: 'scheduler-retrieve',
importance: 0.4,
prompt_path: promptPath,
},
}];
try {
// 1. Ingest seed event
const ingested = await ingestEvents(events, { agent_id: agentId });
// 1. Ingest seed event
const ingested = await ingestEvents(events, { agent_id: agentId });
// 2. Retrieve memories
const retrieved = await getRelevantMemories({ agent_id: agentId, query, k: 5 });
// 2. Retrieve memories
const retrieved = await getRelevantMemories({ agent_id: agentId, query, k: 5 });
// 3. Prepare artifacts
const sessionDir = path.join('.scheduler-memory', runId);
const latestDir = path.join('.scheduler-memory', 'latest', cadence);
// 3. Prepare artifacts
const sessionDir = path.join('.scheduler-memory', runId);
const latestDir = path.join('.scheduler-memory', 'latest', cadence);
mkdirSync(sessionDir, { recursive: true });
mkdirSync(latestDir, { recursive: true });
mkdirSync(sessionDir, { recursive: true });
mkdirSync(latestDir, { recursive: true });
const artifact = {
cadence,
operation: 'retrieve',
runId,
servicePath: 'src/services/memory/index.js#getRelevantMemories',
inputs: {
agentId,
promptPath,
promptIntent,
query,
events: events.length
},
outputs: {
ingestedCount: ingested.length,
retrievedCount: retrieved.length
},
status: 'ok'
};
const artifact = {
cadence,
operation: 'retrieve',
runId,
servicePath: 'src/services/memory/index.js#getRelevantMemories',
inputs: {
agentId,
promptPath,
promptIntent,
query,
events: events.length,
},
outputs: {
ingestedCount: ingested.length,
retrievedCount: retrieved.length,
},
status: 'ok',
};
// 4. Write artifacts
writeFileSync(path.join(sessionDir, 'retrieve.json'), JSON.stringify(artifact, null, 2));
writeFileSync(path.join(sessionDir, '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'));
// 4. Write artifacts
writeFileSync(path.join(sessionDir, 'retrieve.json'), JSON.stringify(artifact, null, 2));
writeFileSync(path.join(sessionDir, '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'));
// 5. Output success marker to stdout (for scheduler verification)
console.log('MEMORY_RETRIEVED');
// 5. Output success marker to stdout (for scheduler verification)
console.log('MEMORY_RETRIEVED');
} catch (error) {
console.error('Memory retrieval failed:', error);
process.exit(1);
console.error('Memory retrieval failed:', error);
process.exit(1);
}
+94 -94
View File
@@ -13,20 +13,20 @@ const runId = process.env.SCHEDULER_RUN_ID ||
`session-${Date.now().toString(36)}`;
// Extract prompt intent
let promptIntent = `scheduler memory store`;
let promptIntent = 'scheduler memory store';
if (promptPath) {
try {
const promptRaw = readFileSync(promptPath, 'utf8');
const promptLines = promptRaw.split(/\r?\n/).map(line => line.trim()).filter(Boolean);
const intentLine = promptLines.find(line => line.startsWith('#') || line.toLowerCase().startsWith('goal'));
if (intentLine) {
promptIntent = intentLine.replace(/^#+\s*/, '');
} else if (promptLines.length > 0) {
promptIntent = promptLines[0];
}
} catch (err) {
console.warn(`Could not read prompt file: ${promptPath}`, err.message);
try {
const promptRaw = readFileSync(promptPath, 'utf8');
const promptLines = promptRaw.split(/\r?\n/).map(line => line.trim()).filter(Boolean);
const intentLine = promptLines.find(line => line.startsWith('#') || line.toLowerCase().startsWith('goal'));
if (intentLine) {
promptIntent = intentLine.replace(/^#+\s*/, '');
} else if (promptLines.length > 0) {
promptIntent = promptLines[0];
}
} catch (err) {
console.warn(`Could not read prompt file: ${promptPath}`, err.message);
}
}
// 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));
if (targetFile) {
try {
if (existsSync(targetFile)) {
memoryContent = readFileSync(targetFile, 'utf8').trim();
console.log(`Loaded memory content from ${targetFile}`);
} else {
console.warn(`Target memory file does not exist: ${targetFile}`);
}
} catch (err) {
console.warn(`Failed to read memory file ${targetFile}:`, err.message);
try {
if (existsSync(targetFile)) {
memoryContent = readFileSync(targetFile, 'utf8').trim();
console.log(`Loaded memory content from ${targetFile}`);
} else {
console.warn(`Target memory file does not exist: ${targetFile}`);
}
} catch (err) {
console.warn(`Failed to read memory file ${targetFile}:`, err.message);
}
}
const baseTs = Date.now();
let events = [];
if (memoryContent) {
events.push({
agent_id: agentId,
content: memoryContent,
timestamp: baseTs,
tags: ['scheduler', cadence, 'store', 'insight'],
metadata: {
session_id: runId,
source: 'agent-output',
importance: 0.8, // User-provided memory is important
prompt_path: promptPath
}
});
events.push({
agent_id: agentId,
content: memoryContent,
timestamp: baseTs,
tags: ['scheduler', cadence, 'store', 'insight'],
metadata: {
session_id: runId,
source: 'agent-output',
importance: 0.8, // User-provided memory is important
prompt_path: promptPath,
},
});
} else {
// 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.`);
events = [
{
agent_id: agentId,
content: `Store memory event A for ${cadence} :: ${promptIntent}`,
timestamp: baseTs,
tags: ['scheduler', cadence, 'store'],
metadata: {
session_id: runId,
source: 'scheduler-store',
importance: 0.55,
prompt_path: promptPath
}
},
{
agent_id: agentId,
content: `Store memory event B for ${cadence} :: ${promptIntent}`,
timestamp: baseTs + 1,
tags: ['scheduler', cadence, 'store'],
metadata: {
session_id: runId,
source: 'scheduler-store',
importance: 0.55,
prompt_path: promptPath
}
}
];
// 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.`);
events = [
{
agent_id: agentId,
content: `Store memory event A for ${cadence} :: ${promptIntent}`,
timestamp: baseTs,
tags: ['scheduler', cadence, 'store'],
metadata: {
session_id: runId,
source: 'scheduler-store',
importance: 0.55,
prompt_path: promptPath,
},
},
{
agent_id: agentId,
content: `Store memory event B for ${cadence} :: ${promptIntent}`,
timestamp: baseTs + 1,
tags: ['scheduler', cadence, 'store'],
metadata: {
session_id: runId,
source: 'scheduler-store',
importance: 0.55,
prompt_path: promptPath,
},
},
];
}
try {
// 1. Ingest events
const stored = await ingestEvents(events, { agent_id: agentId });
// 1. Ingest events
const stored = await ingestEvents(events, { agent_id: agentId });
// 2. Prepare artifacts
const sessionDir = path.join('.scheduler-memory', runId);
const latestDir = path.join('.scheduler-memory', 'latest', cadence);
// 2. Prepare artifacts
const sessionDir = path.join('.scheduler-memory', runId);
const latestDir = path.join('.scheduler-memory', 'latest', cadence);
mkdirSync(sessionDir, { recursive: true });
mkdirSync(latestDir, { recursive: true });
mkdirSync(sessionDir, { recursive: true });
mkdirSync(latestDir, { recursive: true });
const artifact = {
cadence,
operation: 'store',
runId,
servicePath: 'src/services/memory/index.js#ingestEvents',
inputs: {
agentId,
promptPath,
promptIntent,
events: events.length,
sourceFile: targetFile || 'fallback'
},
outputs: {
storedCount: stored.length,
summaries: stored.map(m => m.summary)
},
status: 'ok'
};
const artifact = {
cadence,
operation: 'store',
runId,
servicePath: 'src/services/memory/index.js#ingestEvents',
inputs: {
agentId,
promptPath,
promptIntent,
events: events.length,
sourceFile: targetFile || 'fallback',
},
outputs: {
storedCount: stored.length,
summaries: stored.map(m => m.summary),
},
status: 'ok',
};
// 3. Write artifacts
writeFileSync(path.join(sessionDir, 'store.json'), JSON.stringify(artifact, null, 2));
writeFileSync(path.join(sessionDir, 'store.ok'), 'MEMORY_STORED\n');
writeFileSync(path.join(latestDir, 'store.ok'), 'MEMORY_STORED\n');
// 3. Write artifacts
writeFileSync(path.join(sessionDir, 'store.json'), JSON.stringify(artifact, null, 2));
writeFileSync(path.join(sessionDir, 'store.ok'), 'MEMORY_STORED\n');
writeFileSync(path.join(latestDir, 'store.ok'), 'MEMORY_STORED\n');
// 4. Output success marker to stdout (for scheduler verification)
console.log('MEMORY_STORED');
// 4. Output success marker to stdout (for scheduler verification)
console.log('MEMORY_STORED');
} catch (error) {
console.error('Memory storage failed:', error);
process.exit(1);
console.error('Memory storage failed:', error);
process.exit(1);
}
+1 -1
View File
@@ -24,7 +24,7 @@ async function runTests(i) {
return new Promise((resolve) => {
// Pass strictly the test args to the child process
const child = spawn('node', ['--test', ...testArgs], {
stdio: ['ignore', 'pipe', 'pipe']
stdio: ['ignore', 'pipe', 'pipe'],
});
let stdout = '';
@@ -12,7 +12,7 @@ function getFiles(dir, files = []) {
files.push(name);
}
}
} catch (_e) {
} catch {
// ignore if dir doesn't exist
}
return files;
@@ -45,11 +45,11 @@ for (const file of testFiles) {
// Naive check for assertions: look for assert., expect(, t.
// Many tests use 't' context from node:test
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.
// Let's look for common assertion keywords.
const assertionKeywords = ['assert', 'expect', 'strictEqual', 'deepStrictEqual', 'ok', 'equal'];
const hasAssertion = assertionKeywords.some(k => content.includes(k));
if (!hasAssertion) issues.push('No obvious assertions');
// This is very naive, might false positive on 't.ok' etc.
// Let's look for common assertion keywords.
const assertionKeywords = ['assert', 'expect', 'strictEqual', 'deepStrictEqual', 'ok', 'equal'];
const hasAssertion = assertionKeywords.some(k => content.includes(k));
if (!hasAssertion) issues.push('No obvious assertions');
}
if (issues.length > 0) {
@@ -70,7 +70,7 @@ const invariants = [
'parseFrontmatterAgent(content) || parseAgentFromFilename(latestFile)',
'scheduler.firstPromptByCadence?.[cadence]',
'ALL_EXCLUDED_REASON',
"reason: ALL_EXCLUDED_REASON",
'reason: ALL_EXCLUDED_REASON',
],
},
{
@@ -161,7 +161,7 @@ const [meta, flow, scheduler] = await Promise.all([
const errors = [];
for (const invariant of invariants) {
if (invariant.promptSnippets?.meta) {
if (invariant.promptSnippets?.meta) {
const missingMeta = collectMissing(meta, invariant.promptSnippets.meta);
for (const snippet of missingMeta) {
errors.push(`[${invariant.name}] ${FILES.meta} missing snippet: ${snippet}`);
+1 -1
View File
@@ -67,7 +67,7 @@ export async function cmdBackup({ output = null } = {}) {
await fs.writeFile(
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));
+8 -8
View File
@@ -8,14 +8,14 @@ import { todayDateStr as _todayDateStr, nowUnix as _nowUnix } from './utils.mjs'
export async function cmdList(cadence, deps = {}) {
const {
getRelays = _getRelays,
getNamespace = _getNamespace,
getRoster = _getRoster,
queryLocks = _queryLocks,
todayDateStr = _todayDateStr,
nowUnix = _nowUnix,
log = console.log,
error = console.error,
getRelays = _getRelays,
getNamespace = _getNamespace,
getRoster = _getRoster,
queryLocks = _queryLocks,
todayDateStr = _todayDateStr,
nowUnix = _nowUnix,
log = console.log,
error = console.error,
} = deps;
const relays = await getRelays();
+25 -25
View File
@@ -4,19 +4,19 @@ import fs from 'node:fs/promises';
export async function cmdProposal(subcommand, args = {}) {
switch (subcommand) {
case 'create':
return await handleCreate(args);
case 'list':
return await handleList(args);
case 'apply':
return await handleApply(args);
case 'reject':
return await handleReject(args);
case 'show':
return await handleShow(args);
default:
console.error(`Unknown proposal subcommand: ${subcommand}`);
throw new ExitError(1, 'Unknown subcommand');
case 'create':
return handleCreate(args);
case 'list':
return handleList(args);
case 'apply':
return handleApply(args);
case 'reject':
return handleReject(args);
case 'show':
return handleShow(args);
default:
console.error(`Unknown proposal subcommand: ${subcommand}`);
throw new ExitError(1, 'Unknown subcommand');
}
}
@@ -29,7 +29,7 @@ async function handleCreate({ agent, target, contentFile, reason }) {
let newContent;
try {
newContent = await fs.readFile(contentFile, 'utf8');
} catch (_e) {
} catch {
console.error(`Failed to read content file: ${contentFile}`);
throw new ExitError(1, 'File read error');
}
@@ -85,15 +85,15 @@ async function handleReject({ id, reason }) {
}
async function handleShow({ id }) {
if (!id) {
console.error('Usage: torch-lock proposal show --id <proposal-id>');
throw new ExitError(1, 'Missing id');
}
try {
const proposal = await getProposal(id);
console.log(JSON.stringify(proposal, null, 2));
} catch (e) {
console.error(`Failed to show proposal: ${e.message}`);
throw new ExitError(1, 'Show failed');
}
if (!id) {
console.error('Usage: torch-lock proposal show --id <proposal-id>');
throw new ExitError(1, 'Missing id');
}
try {
const proposal = await getProposal(id);
console.log(JSON.stringify(proposal, null, 2));
} catch (e) {
console.error(`Failed to show proposal: ${e.message}`);
throw new ExitError(1, 'Show failed');
}
}
+138 -138
View File
@@ -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';",
'X-Content-Type-Options': 'nosniff',
'X-Frame-Options': 'DENY',
'Referrer-Policy': 'strict-origin-when-cross-origin'
'Referrer-Policy': 'strict-origin-when-cross-origin',
};
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) => {
try {
// Basic Auth check
if (auth) {
const authHeader = req.headers.authorization;
if (!authHeader) {
res.writeHead(401, { ...SECURITY_HEADERS, 'WWW-Authenticate': 'Basic realm="TORCH Dashboard"' });
res.end('Authentication required');
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;
if (auth) {
const authHeader = req.headers.authorization;
if (!authHeader) {
res.writeHead(401, { ...SECURITY_HEADERS, 'WWW-Authenticate': 'Basic realm="TORCH Dashboard"' });
res.end('Authentication required');
return;
}
}
if (!isValid) {
res.writeHead(401, { ...SECURITY_HEADERS, 'WWW-Authenticate': 'Basic realm="TORCH Dashboard"' });
res.end('Invalid 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');
const parts = authHeader.split(' ');
const type = parts[0];
const credentials = parts[1];
let isValid = false;
if (type === 'Basic' && credentials) {
try {
configContent = await fsp.readFile(packageConfigPath, 'utf8');
const decoded = Buffer.from(credentials, 'base64').toString();
isValid = timingSafeCompare(decoded, auth);
} 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) {
try {
const rawConfig = JSON.parse(configContent);
const safeConfig = parseTorchConfig(rawConfig);
// URL parsing
const url = new URL(req.url, `http://${req.headers.host}`);
const pathname = url.pathname;
// Security: Whitelist only fields required by the dashboard frontend
const publicConfig = {
dashboard: safeConfig.dashboard ? { ...safeConfig.dashboard } : {},
nostrLock: safeConfig.nostrLock
? {
// 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 {
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,
relays: safeConfig.nostrLock.relays,
}
: {},
};
: {},
};
// Remove sensitive dashboard auth
if (publicConfig.dashboard.auth) {
delete publicConfig.dashboard.auth;
// Remove sensitive dashboard auth
if (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;
}
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');
} else {
res.writeHead(404, SECURITY_HEADERS);
res.end('Not Found');
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.end('Not Found');
return;
}
}
// 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.end('Not Found');
return;
}
// MIME types
const extname = path.extname(filePath);
let contentType = 'text/plain';
switch (extname) {
// MIME types
const extname = path.extname(filePath);
let contentType = 'text/plain';
switch (extname) {
case '.html': contentType = 'text/html'; break;
case '.js': 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 '.ico': contentType = 'image/x-icon'; break;
case '.md': contentType = 'text/markdown'; break;
}
}
res.writeHead(200, { ...SECURITY_HEADERS, 'Content-Type': contentType });
fs.createReadStream(filePath).pipe(res);
res.writeHead(200, { ...SECURITY_HEADERS, 'Content-Type': contentType });
fs.createReadStream(filePath).pipe(res);
} catch (err) {
console.error('Dashboard Server Error:', err);
if (!res.headersSent) {
+179 -179
View File
@@ -182,7 +182,7 @@ export async function cmdLock(agent, cadence, optionsOrDryRun = false, deps = {}
raceCheckDelayMs = RACE_CHECK_DELAY_MS,
getDateStr = todayDateStr,
log = console.log,
error = console.error
error = console.error,
} = deps;
const relays = await getRelays();
@@ -237,10 +237,10 @@ export async function cmdLock(agent, cadence, optionsOrDryRun = false, deps = {}
// Check if it is a completed task
if (earliest.status === 'completed') {
error(`LOCK DENIED: Task already completed by event ${earliest.eventId}`);
log('LOCK_STATUS=denied');
log('LOCK_REASON=already_completed');
throw new ExitError(3, 'Task already completed');
error(`LOCK DENIED: Task already completed by event ${earliest.eventId}`);
log('LOCK_STATUS=denied');
log('LOCK_REASON=already_completed');
throw new ExitError(3, 'Task already completed');
}
error(
@@ -349,7 +349,7 @@ export async function cmdList(cadence, deps = {}) {
getRoster = _getRoster,
getDateStr = todayDateStr,
log = console.log,
error = console.error
error = console.error,
} = deps;
const relays = await getRelays();
@@ -383,10 +383,10 @@ export async function cmdList(cadence, deps = {}) {
let remainMin = '?';
if (lock.status === 'completed') {
remainMin = 'done';
remainMin = 'done';
} else if (lock.expiresAt) {
const remaining = lock.expiresAt - nowUnix();
remainMin = Math.round(remaining / 60);
const remaining = lock.expiresAt - nowUnix();
remainMin = Math.round(remaining / 60);
}
log(
@@ -443,7 +443,7 @@ export async function cmdComplete(agent, cadence, optionsOrDryRun = false, deps
finalizeEvent = _finalizeEvent,
getDateStr = todayDateStr,
log = console.log,
error = console.error
error = console.error,
} = deps;
const relays = await getRelays();
@@ -461,7 +461,7 @@ export async function cmdComplete(agent, cadence, optionsOrDryRun = false, deps
if (!myLock) {
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');
}
@@ -544,204 +544,204 @@ export async function main(argv) {
}
switch (args.command) {
case 'check': {
if (!args.cadence || !VALID_CADENCES.has(args.cadence)) {
console.error(`ERROR: --cadence <${[...VALID_CADENCES].join('|')}> is required for check`);
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;
case 'check': {
if (!args.cadence || !VALID_CADENCES.has(args.cadence)) {
console.error(`ERROR: --cadence <${[...VALID_CADENCES].join('|')}> is required for check`);
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;
}
case 'lock': {
if (!args.agent) {
console.error('ERROR: --agent <name> is required for lock');
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;
case 'lock': {
if (!args.agent) {
console.error('ERROR: --agent <name> is required for lock');
throw new ExitError(1, 'Missing agent');
}
case 'complete': {
if (!args.agent) {
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;
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;
}
case 'list': {
if (args.cadence && !VALID_CADENCES.has(args.cadence)) {
console.error(`ERROR: --cadence must be one of: ${[...VALID_CADENCES].join(', ')}`);
throw new ExitError(1, 'Invalid cadence');
}
await cmdList(args.cadence || null);
break;
case 'complete': {
if (!args.agent) {
console.error('ERROR: --agent <name> is required for complete');
throw new ExitError(1, 'Missing agent');
}
case 'health': {
if (!args.cadence || !VALID_CADENCES.has(args.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;
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;
}
case 'dashboard': {
await cmdDashboard(args.port, args.host);
break;
case 'list': {
if (args.cadence && !VALID_CADENCES.has(args.cadence)) {
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': {
await cmdInit(args.force);
break;
case 'health': {
if (!args.cadence || !VALID_CADENCES.has(args.cadence)) {
console.error(`ERROR: --cadence <${[...VALID_CADENCES].join('|')}> is required for health`);
throw new ExitError(1, 'Missing cadence');
}
case 'update': {
await cmdUpdate(args.force);
break;
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;
}
case 'remove': {
await cmdRemove(args.force);
break;
}
case 'dashboard': {
await cmdDashboard(args.port, args.host);
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,
});
case 'init': {
await cmdInit(args.force);
break;
}
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)';
}
case 'update': {
await cmdUpdate(args.force);
break;
}
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': {
if (!args.id) {
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;
}
console.log(JSON.stringify(result, null, 2));
break;
}
case 'pin-memory': {
if (!args.id) {
console.error('ERROR: --id <memoryId> is required for pin-memory');
throw new ExitError(1, 'Missing memory id');
}
const result = await pinMemory(args.id);
console.log(JSON.stringify(result, null, 2));
break;
case 'inspect-memory': {
if (!args.id) {
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 'unpin-memory': {
if (!args.id) {
console.error('ERROR: --id <memoryId> is required for unpin-memory');
throw new ExitError(1, 'Missing memory id');
}
const result = await unpinMemory(args.id);
console.log(JSON.stringify(result, null, 2));
break;
case 'pin-memory': {
if (!args.id) {
console.error('ERROR: --id <memoryId> is required for pin-memory');
throw new ExitError(1, 'Missing memory id');
}
const result = await pinMemory(args.id);
console.log(JSON.stringify(result, null, 2));
break;
}
case 'trigger-prune-dry-run': {
const result = await triggerPruneDryRun({ retentionMs: args.retentionMs ?? undefined });
console.log(JSON.stringify(result, null, 2));
break;
case 'unpin-memory': {
if (!args.id) {
console.error('ERROR: --id <memoryId> is required for unpin-memory');
throw new ExitError(1, 'Missing memory id');
}
const result = await unpinMemory(args.id);
console.log(JSON.stringify(result, null, 2));
break;
}
case 'memory-stats': {
const result = await memoryStats({ windowMs: args.windowMs ?? undefined });
console.log(JSON.stringify(result, null, 2));
break;
case 'trigger-prune-dry-run': {
const result = await triggerPruneDryRun({ retentionMs: args.retentionMs ?? undefined });
console.log(JSON.stringify(result, null, 2));
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': {
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 'rollback': {
await cmdRollback(args.target, args.strategy, { list: args.list });
break;
}
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;
}
case 'rollback': {
await cmdRollback(args.target, args.strategy, { list: args.list });
break;
}
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');
default:
console.error(`ERROR: Unknown command: ${args.command}`);
usage();
throw new ExitError(1, 'Unknown command');
}
} catch (err) {
if (err instanceof ExitError) {
+87 -87
View File
@@ -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'];
function getPaths(root, installDirName) {
const torchDir = path.resolve(root, installDirName);
return {
root,
torchDir,
promptsDir: path.join(torchDir, 'prompts'),
roster: path.join(torchDir, 'roster.json'),
};
const torchDir = path.resolve(root, installDirName);
return {
root,
torchDir,
promptsDir: path.join(torchDir, 'prompts'),
roster: path.join(torchDir, 'roster.json'),
};
}
function copyDir(src, dest) {
if (fs.existsSync(src)) {
fs.cpSync(src, dest, { recursive: true });
}
if (fs.existsSync(src)) {
fs.cpSync(src, dest, { recursive: true });
}
}
function transformContent(content, installDirName) {
@@ -176,10 +176,10 @@ async function resolveConfiguration(cwd, mockAnswers) {
function ensureInstallDirectory(paths, force, installDir) {
if (fs.existsSync(paths.torchDir) && !force) {
const entries = fs.readdirSync(paths.torchDir);
if (entries.length > 0 && installDir !== '.') {
throw new Error(`Directory ${paths.torchDir} already exists and is not empty. Use --force to overwrite.`);
}
const entries = fs.readdirSync(paths.torchDir);
if (entries.length > 0 && installDir !== '.') {
throw new Error(`Directory ${paths.torchDir} already exists and is not empty. Use --force to overwrite.`);
}
}
ensureDir(paths.torchDir);
ensureDir(paths.promptsDir);
@@ -249,17 +249,17 @@ function configureTorch(cwd, paths, installDir, namespace, relays, hashtag) {
// Try to load existing or example
if (fs.existsSync(configPath)) {
try {
configData = JSON.parse(fs.readFileSync(configPath, 'utf8'));
console.log(`Updating existing ${path.relative(cwd, configPath)}...`);
} catch (e) {
console.warn(`Could not parse existing config: ${e.message}`);
}
try {
configData = JSON.parse(fs.readFileSync(configPath, 'utf8'));
console.log(`Updating existing ${path.relative(cwd, configPath)}...`);
} catch (e) {
console.warn(`Could not parse existing config: ${e.message}`);
}
} else {
const exampleConfigPath = path.join(PKG_ROOT, 'torch-config.example.json');
if (fs.existsSync(exampleConfigPath)) {
configData = JSON.parse(fs.readFileSync(exampleConfigPath, 'utf8'));
}
const exampleConfigPath = path.join(PKG_ROOT, 'torch-config.example.json');
if (fs.existsSync(exampleConfigPath)) {
configData = JSON.parse(fs.readFileSync(exampleConfigPath, 'utf8'));
}
}
// Apply user choices
@@ -286,11 +286,11 @@ function configureTorch(cwd, paths, installDir, namespace, relays, hashtag) {
['daily', 'weekly'].forEach(cadence => {
if (!configData.scheduler.memoryPolicyByCadence[cadence]) {
configData.scheduler.memoryPolicyByCadence[cadence] = {
mode: "required",
retrieveSuccessMarkers: ["MEMORY_RETRIEVED"],
storeSuccessMarkers: ["MEMORY_STORED"],
mode: 'required',
retrieveSuccessMarkers: ['MEMORY_RETRIEVED'],
storeSuccessMarkers: ['MEMORY_STORED'],
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
// If we are NOT installing to '.', the host package.json is in paths.root
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) {
const hostPkgPath = path.join(hostRoot, 'package.json');
if (!fs.existsSync(hostPkgPath)) {
console.warn('No package.json found in host root. Skipping script injection.');
return;
const hostPkgPath = path.join(hostRoot, 'package.json');
if (!fs.existsSync(hostPkgPath)) {
console.warn('No package.json found in host root. Skipping script injection.');
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 {
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.`);
}
}
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}`);
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}`);
}
}
/**
@@ -431,7 +431,7 @@ function detectTorchInstallDir(cwd) {
if (pkg.name === 'torch-lock') {
return '.';
}
} catch (_e) {
} catch {
// Ignore parse errors
}
}
@@ -571,7 +571,7 @@ export async function cmdRemove(force = false, cwd = process.cwd(), mockAnswers
if (pkg.scripts) {
hasHostScripts = TORCH_HOST_SCRIPTS.some(key => key in pkg.scripts);
}
} catch (_e) { /* ignore */ }
} catch { /* ignore */ }
}
if (targets.length === 0 && !hasHostScripts) {
@@ -628,7 +628,7 @@ export async function cmdRemove(force = false, cwd = process.cwd(), mockAnswers
fs.rmdirSync(srcDir);
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
@@ -664,15 +664,15 @@ export function cmdUpdate(force = false, cwd = process.cwd()) {
let installDirName = 'torch';
if (!fs.existsSync(path.join(cwd, 'torch')) && fs.existsSync(path.join(cwd, 'package.json'))) {
// Check if current dir is the torch dir
try {
const pkg = JSON.parse(fs.readFileSync(path.join(cwd, 'package.json'), 'utf8'));
if (pkg.name === 'torch-lock') {
installDirName = '.';
}
} catch (_e) {
// Ignore error if package.json is missing or invalid
// Check if current dir is the torch dir
try {
const pkg = JSON.parse(fs.readFileSync(path.join(cwd, 'package.json'), 'utf8'));
if (pkg.name === 'torch-lock') {
installDirName = '.';
}
} catch {
// Ignore error if package.json is missing or invalid
}
}
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
const entries = fs.readdirSync(paths.torchDir);
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 destPath = path.join(thisBackupDir, entry);
fs.cpSync(srcPath, destPath, { recursive: true });
const srcPath = path.join(paths.torchDir, entry);
const destPath = path.join(thisBackupDir, entry);
fs.cpSync(srcPath, destPath, { recursive: true });
}
// 2. Update App Directories (Overwrite)
@@ -746,15 +746,15 @@ export function cmdUpdate(force = false, cwd = process.cwd()) {
const destFile = path.join(destDir, file);
if (force) {
copyFile(srcFile, destFile, false, true, installDirName);
updated++;
copyFile(srcFile, destFile, false, true, installDirName);
updated++;
} else {
if (!fs.existsSync(destFile)) {
copyFile(srcFile, destFile, false, true, installDirName);
added++;
} else {
skipped++;
}
if (!fs.existsSync(destFile)) {
copyFile(srcFile, destFile, false, true, installDirName);
added++;
} else {
skipped++;
}
}
}
console.log(` ${dir}/: ${added} added, ${updated} updated, ${skipped} preserved`);
+20 -20
View File
@@ -9,9 +9,9 @@ const originalConfig = fs.existsSync(configPath) ? fs.readFileSync(configPath) :
if (!fs.existsSync(configPath)) {
fs.writeFileSync(configPath, JSON.stringify({
nostrLock: {
relays: ["wss://relay.damus.io"],
namespace: "test-ns"
}
relays: ['wss://relay.damus.io'],
namespace: 'test-ns',
},
}, null, 2));
}
@@ -19,27 +19,27 @@ const ITERATIONS = 1000;
const start = process.hrtime.bigint();
async function run() {
for (let i = 0; i < ITERATIONS; i++) {
_resetTorchConfigCache();
try {
const res = await loadTorchConfig();
// Use res to prevent dead code elimination if JIT is smart (though side effects exist)
if (!res) throw new Error("Failed");
} catch (_e) {
// ignore
}
for (let i = 0; i < ITERATIONS; i++) {
_resetTorchConfigCache();
try {
const res = await loadTorchConfig();
// Use res to prevent dead code elimination if JIT is smart (though side effects exist)
if (!res) throw new Error('Failed');
} catch {
// ignore
}
}
const end = process.hrtime.bigint();
const duration = Number(end - start) / 1e6; // ms
const end = process.hrtime.bigint();
const duration = Number(end - start) / 1e6; // 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] Loaded config ${ITERATIONS} times in ${duration.toFixed(2)}ms`);
console.log(`[ASYNC] Average: ${(duration / ITERATIONS).toFixed(4)}ms per load`);
// Cleanup
if (!originalConfig && fs.existsSync(configPath)) {
fs.unlinkSync(configPath);
}
// Cleanup
if (!originalConfig && fs.existsSync(configPath)) {
fs.unlinkSync(configPath);
}
}
run();
+12 -12
View File
@@ -28,9 +28,9 @@ export async function listProposals() {
const meta = JSON.parse(metaContent);
proposals.push({
id: entry.name,
...meta
...meta,
});
} catch (_e) {
} catch {
// Ignore invalid proposals
}
}
@@ -59,7 +59,7 @@ export async function getProposal(id) {
meta,
newContent,
diff,
dir
dir,
};
} catch (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) {
diff = e.stdout.toString();
} else if (e.status !== 1) {
// Some other error
console.error('Diff generation failed:', e.message);
// Some other error
console.error('Diff generation failed:', e.message);
}
}
} catch (_err) {
} catch {
diff = '(New File)';
}
@@ -119,7 +119,7 @@ export async function createProposal({ agent, target, newContent, reason }) {
target,
reason,
timestamp: new Date().toISOString(),
status: 'pending'
status: 'pending',
};
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) {
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(),
};
await fs.writeFile(archivePath.replace(/\.md$/, '.meta.json'), JSON.stringify(archiveMeta, null, 2));
} catch (_e) {
} catch {
// 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
archivedAt = tsPart.replace(
/^(\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
} else {
@@ -295,7 +295,7 @@ export async function listPromptVersions(target) {
reason: meta?.reason ?? null,
});
}
} catch (_e) {
} catch {
// 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');
}
}
} catch (_e) {
} catch {
// Archive lookup failed
}
+1 -1
View File
@@ -113,7 +113,7 @@ function toContextString(agentContext) {
return agentContext.trim();
}
if (agentContext == null) {
if (agentContext === null || agentContext === undefined) {
return '';
}
+1 -1
View File
@@ -197,7 +197,7 @@ export async function createLifecyclePlan(memories, options) {
const templates = loadMemoryPromptTemplates({ templateDir: options.templateDir });
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();
+2 -2
View File
@@ -54,8 +54,8 @@ function textMatchSimilarity(queryTerms, queryRegexes, memory) {
*/
function matchesMetadataFilters(memory, params) {
const inTimeframe = !params.timeframe
|| ((params.timeframe.from == null || memory.created_at >= params.timeframe.from)
&& (params.timeframe.to == null || memory.created_at <= params.timeframe.to));
|| ((params.timeframe.from === null || memory.created_at >= params.timeframe.from)
&& (params.timeframe.to === null || memory.created_at <= params.timeframe.to));
if (!inTimeframe) return false;
+3 -3
View File
@@ -25,7 +25,7 @@ function redactObviousPii(text) {
function renderTemplate(template, replacements) {
return Object.entries(replacements).reduce(
(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 content = readFileSync(filePath, 'utf8').trim();
return [key, content];
})
}),
);
templateCache.set(templateDir, templates);
@@ -77,7 +77,7 @@ function isLikelyFactual(summary, sourceText) {
sourceText
.toLowerCase()
.split(/[^a-z0-9]+/)
.filter((token) => token.length > 3)
.filter((token) => token.length > 3),
);
const summaryTokens = summary
.toLowerCase()
+30
View File
@@ -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);
});
});
});
+83
View File
@@ -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);
});
});
+35
View File
@@ -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;
}
}
});
});
});