@1mancompany/onemancompany 0.7.19 → 0.7.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/frontend/app.js +17 -0
- package/package.json +1 -1
- package/pyproject.toml +1 -1
- package/src/onemancompany/api/routes.py +33 -17
package/frontend/app.js
CHANGED
|
@@ -795,6 +795,9 @@ class AppController {
|
|
|
795
795
|
// Create Product modal
|
|
796
796
|
this._initCreateProductModal();
|
|
797
797
|
|
|
798
|
+
// Product selector feedback
|
|
799
|
+
this._initProductSelector();
|
|
800
|
+
|
|
798
801
|
hrBtn?.addEventListener('click', () => {
|
|
799
802
|
hrBtn.disabled = true;
|
|
800
803
|
this.logEntry('CEO', '🔄 Triggering quarterly review...', 'ceo');
|
|
@@ -7262,6 +7265,20 @@ class AppController {
|
|
|
7262
7265
|
}
|
|
7263
7266
|
|
|
7264
7267
|
// ===== Product Selector =====
|
|
7268
|
+
_initProductSelector() {
|
|
7269
|
+
const sel = document.getElementById('ceo-product-select');
|
|
7270
|
+
if (!sel) return;
|
|
7271
|
+
sel.addEventListener('change', () => {
|
|
7272
|
+
if (sel.value) {
|
|
7273
|
+
sel.style.borderColor = 'var(--pixel-cyan)';
|
|
7274
|
+
sel.style.color = 'var(--pixel-white)';
|
|
7275
|
+
} else {
|
|
7276
|
+
sel.style.borderColor = '';
|
|
7277
|
+
sel.style.color = '';
|
|
7278
|
+
}
|
|
7279
|
+
});
|
|
7280
|
+
}
|
|
7281
|
+
|
|
7265
7282
|
async _refreshProductSelector() {
|
|
7266
7283
|
try {
|
|
7267
7284
|
const data = await fetch('/api/products').then(r => r.json());
|
package/package.json
CHANGED
package/pyproject.toml
CHANGED
|
@@ -655,7 +655,7 @@ async def ceo_submit_task(
|
|
|
655
655
|
|
|
656
656
|
@router.post("/api/task/{project_id}/followup")
|
|
657
657
|
async def task_followup(project_id: str, body: dict) -> dict:
|
|
658
|
-
"""CEO adds follow-up instructions to an existing task, dispatched to EA with context."""
|
|
658
|
+
"""CEO adds follow-up instructions to an existing task, dispatched to assignee (product owner or EA) with context."""
|
|
659
659
|
from datetime import datetime as _dt
|
|
660
660
|
|
|
661
661
|
from onemancompany.core.agent_loop import get_agent_loop
|
|
@@ -699,7 +699,23 @@ async def task_followup(project_id: str, body: dict) -> dict:
|
|
|
699
699
|
for fname in deliverables:
|
|
700
700
|
work_summary_lines.append(f" {fname}")
|
|
701
701
|
|
|
702
|
-
#
|
|
702
|
+
# Determine assignee: product owner if product-linked, else EA
|
|
703
|
+
assignee_id = EA_ID
|
|
704
|
+
product_id = doc.get("product_id", "")
|
|
705
|
+
if product_id:
|
|
706
|
+
from onemancompany.core.product import find_slug_by_product_id, load_product
|
|
707
|
+
product_slug = find_slug_by_product_id(product_id)
|
|
708
|
+
if product_slug:
|
|
709
|
+
product = load_product(product_slug)
|
|
710
|
+
if product and product.get("owner_id"):
|
|
711
|
+
assignee_id = product["owner_id"]
|
|
712
|
+
logger.debug("[FOLLOWUP] Product-linked project {}, routing to owner {}",
|
|
713
|
+
project_id, assignee_id)
|
|
714
|
+
if assignee_id == EA_ID:
|
|
715
|
+
logger.debug("[FOLLOWUP] No product owner found for project {}, falling back to EA",
|
|
716
|
+
project_id)
|
|
717
|
+
|
|
718
|
+
# Build follow-up task for assignee
|
|
703
719
|
context_parts = [
|
|
704
720
|
f"CEO has added follow-up instructions to a completed task:\n",
|
|
705
721
|
f"Original task: {original_task}\n",
|
|
@@ -721,11 +737,11 @@ async def task_followup(project_id: str, body: dict) -> dict:
|
|
|
721
737
|
else:
|
|
722
738
|
tree = TaskTree(project_id=project_id)
|
|
723
739
|
|
|
724
|
-
|
|
725
|
-
if not
|
|
726
|
-
raise HTTPException(status_code=503, detail="
|
|
740
|
+
assignee_loop = get_agent_loop(assignee_id)
|
|
741
|
+
if not assignee_loop:
|
|
742
|
+
raise HTTPException(status_code=503, detail=f"Agent {assignee_id} not available")
|
|
727
743
|
|
|
728
|
-
schedule_node_id = "" # will be set to the
|
|
744
|
+
schedule_node_id = "" # will be set to the assignee node to schedule
|
|
729
745
|
|
|
730
746
|
if tree.root_id:
|
|
731
747
|
# Add a new subtree from CEO root — old subtree stays intact
|
|
@@ -741,39 +757,39 @@ async def task_followup(project_id: str, body: dict) -> dict:
|
|
|
741
757
|
followup_node.node_type = NodeType.CEO_FOLLOWUP
|
|
742
758
|
followup_node.status = TaskPhase.ACCEPTED.value
|
|
743
759
|
|
|
744
|
-
# Create
|
|
745
|
-
|
|
760
|
+
# Create execution node under the followup node
|
|
761
|
+
exec_child = tree.add_child(
|
|
746
762
|
parent_id=followup_node.id,
|
|
747
|
-
employee_id=
|
|
763
|
+
employee_id=assignee_id,
|
|
748
764
|
description=followup_task,
|
|
749
765
|
acceptance_criteria=[],
|
|
750
766
|
)
|
|
751
|
-
schedule_node_id =
|
|
767
|
+
schedule_node_id = exec_child.id
|
|
752
768
|
|
|
753
769
|
# Keep CEO root in PROCESSING while new subtree runs
|
|
754
770
|
if root and root.node_type == NodeType.CEO_PROMPT:
|
|
755
771
|
root.status = TaskPhase.PROCESSING.value
|
|
756
772
|
else:
|
|
757
|
-
# No root yet — create CEO root +
|
|
773
|
+
# No root yet — create CEO root + assignee child
|
|
758
774
|
ceo_root = tree.create_root(employee_id=CEO_ID, description=instructions)
|
|
759
775
|
ceo_root.node_type = NodeType.CEO_PROMPT
|
|
760
776
|
ceo_root.set_status(TaskPhase.PROCESSING)
|
|
761
|
-
|
|
777
|
+
exec_child = tree.add_child(
|
|
762
778
|
parent_id=ceo_root.id,
|
|
763
|
-
employee_id=
|
|
779
|
+
employee_id=assignee_id,
|
|
764
780
|
description=instructions,
|
|
765
781
|
acceptance_criteria=[],
|
|
766
782
|
)
|
|
767
|
-
schedule_node_id =
|
|
783
|
+
schedule_node_id = exec_child.id
|
|
768
784
|
|
|
769
785
|
_save_project_tree(pdir, tree)
|
|
770
786
|
|
|
771
|
-
# Schedule the
|
|
787
|
+
# Schedule the assignee node for execution
|
|
772
788
|
if schedule_node_id:
|
|
773
789
|
tree_path = str(Path(pdir) / TASK_TREE_FILENAME)
|
|
774
790
|
from onemancompany.core.agent_loop import employee_manager
|
|
775
|
-
employee_manager.schedule_node(
|
|
776
|
-
employee_manager._schedule_next(
|
|
791
|
+
employee_manager.schedule_node(assignee_id, schedule_node_id, tree_path)
|
|
792
|
+
employee_manager._schedule_next(assignee_id)
|
|
777
793
|
|
|
778
794
|
# Update project.yaml status back to in_progress
|
|
779
795
|
doc["status"] = "in_progress"
|