ofiere-openclaw-plugin 4.32.0 → 4.33.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ofiere-openclaw-plugin",
3
- "version": "4.32.0",
3
+ "version": "4.33.0",
4
4
  "type": "module",
5
5
  "description": "OpenClaw plugin for Ofiere PM - 14 meta-tools covering tasks, agents, projects, scheduling, knowledge, workflows, notifications, memory, prompts, constellation, space file management, execution plan builder, SOP management, and agent brain (memory + self-improvement)",
6
6
  "keywords": ["openclaw", "ofiere", "project-management", "agents", "plugin"],
package/src/prompt.ts CHANGED
@@ -159,7 +159,7 @@ ${toolIndex}
159
159
  - CONSTELLATION: ALWAYS read_blueprint before creating/editing agents. ALWAYS confirm before delete.
160
160
  - WORKFLOWS: ALWAYS "get" before modifying. Use "insert_node_between" for mid-flow additions.
161
161
  - CHANNEL REPORTS: ALWAYS include agent_id (YOUR name) in send_report. Use get_task_detail for drill-down.
162
- - PLANNING GATE: Before creating tasks with 3+ steps or due_dates, ask if user wants a Plan first.
162
+ - **PLANNING GATE (MANDATORY):** Before creating ANY task with 3+ execution steps, goals, constraints, OR a due_date, you MUST ask the user: "Would you like me to plan this first, or create the task directly?" If the user chooses to plan, use OFIERE_PLAN_OPS to create a visual plan first, then execute it to generate the real tasks. Do NOT skip this step.
163
163
  - File refs @[name](file:ID): Use OFIERE_FILE_OPS read_text_file to retrieve — don't ask user.
164
164
  - Task approvals (OFIERE_TASK_OPS) ≠ workflow gate approvals (human_approval nodes).
165
165
 
package/src/tools.ts CHANGED
@@ -198,7 +198,7 @@ function registerTaskOps(
198
198
  `Actions:\n` +
199
199
  `- "list": List/filter tasks. Optional: status, agent_id, space_id, folder_id, task_id, limit\n` +
200
200
  `- "get": Get a single task by ID. Required: task_id\n` +
201
- `- "create": Create a task. Required: title. Optional: agent_id, description, status, priority, space_id, folder_id, start_date, due_date, tags, instructions, execution_plan, goals, constraints, system_prompt, recurrence_type, recurrence_interval, scheduled_time\n` +
201
+ `- "create": Create a task. Required: title. Optional: agent_id, description, status, priority, space_id, folder_id, start_date, due_date, tags, instructions, execution_plan, goals, constraints, system_prompt, recurrence_type, recurrence_interval, scheduled_time. ⚠️ PLANNING GATE: If the task has 3+ execution steps, goals, or constraints, FIRST ask the user "Plan first or create directly?" If they choose to plan, use OFIERE_PLAN_OPS instead.\n` +
202
202
  `- "update": Update a task. Required: task_id. Optional: all create fields + progress\n` +
203
203
  `- "delete": Delete task + subtasks. Required: task_id\n` +
204
204
  `- "add_approval": Request approval on a task. Required: task_id, approver_name. Optional: approver_type (human|agent, auto-detected), due_date, comment\n` +
@@ -241,7 +241,7 @@ function registerTaskOps(
241
241
  folder_id: { type: "string", description: "PM Folder ID" },
242
242
  start_date: { type: "string", description: "Start date (ISO 8601). Required for scheduled/recurring tasks." },
243
243
  due_date: { type: "string", description: "Due date (ISO 8601)" },
244
- scheduled_time: { type: "string", description: "Time to execute in HH:MM format (UTC). If omitted, extracted from start_date or defaults to now+60s." },
244
+ scheduled_time: { type: "string", description: "Time to execute in HH:MM format (user's LOCAL time, e.g. 10:00 for 10 AM WIB). The system converts to UTC automatically." },
245
245
  recurrence_type: {
246
246
  type: "string",
247
247
  description: "How often the task recurs. 'none' for one-shot.",
@@ -542,8 +542,12 @@ async function handleCreateTask(
542
542
  // ── Auto-create scheduler event if task has a start_date ──────────────
543
543
  // This bridges the plugin → scheduler so the pg_cron task-dispatcher
544
544
  // Edge Function picks up the task at the right time.
545
+ //
546
+ // TIMEZONE: scheduled_time is treated as the user's LOCAL time (default WIB, UTC+7).
547
+ // We convert to UTC epoch for next_run_at so the edge function fires correctly.
545
548
  const startDate = params.start_date as string | undefined;
546
549
  const effectiveAgentId = (insertData.agent_id as string) || assignee;
550
+ const WIB_OFFSET_HOURS = 7; // Asia/Jakarta = UTC+7
547
551
  if (startDate && effectiveAgentId) {
548
552
  try {
549
553
  // Parse start_date robustly — it can be:
@@ -563,23 +567,41 @@ async function handleCreateTask(
563
567
  const hasTimeInfo = /[T ]\d{2}:\d{2}/.test(startDate);
564
568
 
565
569
  if (explicitScheduledTime) {
566
- // Agent explicitly passed a scheduled_time — use date from start_date + explicit time
570
+ // Agent explicitly passed a scheduled_time — treat as WIB local time
567
571
  const dateStr = parsedDate.toISOString().split("T")[0]; // YYYY-MM-DD
568
- const dt = new Date(`${dateStr}T${explicitScheduledTime}:00Z`);
572
+ const [localH, localM] = explicitScheduledTime.split(":").map(Number);
573
+ const utcH = localH - WIB_OFFSET_HOURS;
574
+ const dt = new Date(`${dateStr}T00:00:00Z`);
575
+ dt.setUTCHours(utcH, localM, 0, 0);
569
576
  nextRunAtEpoch = Math.floor(dt.getTime() / 1000);
570
- scheduledTimeFinal = explicitScheduledTime;
577
+ scheduledTimeFinal = explicitScheduledTime; // Store as user's local time
571
578
  scheduledDateFinal = dateStr;
572
579
  } else if (hasTimeInfo) {
573
- // start_date already contains time — use it directly
574
- nextRunAtEpoch = Math.floor(parsedDate.getTime() / 1000);
575
- scheduledTimeFinal = `${String(parsedDate.getUTCHours()).padStart(2, "0")}:${String(parsedDate.getUTCMinutes()).padStart(2, "0")}`;
576
- scheduledDateFinal = parsedDate.toISOString().split("T")[0];
580
+ // start_date already contains time — assume it's in the user's local timezone (WIB)
581
+ // Extract the local hour:minute from the string, NOT from UTC parsing
582
+ const timeMatch = startDate.match(/(\d{2}):(\d{2})/);
583
+ if (timeMatch) {
584
+ const localH = parseInt(timeMatch[1], 10);
585
+ const localM = parseInt(timeMatch[2], 10);
586
+ const dateStr = parsedDate.toISOString().split("T")[0];
587
+ const dt = new Date(`${dateStr}T00:00:00Z`);
588
+ dt.setUTCHours(localH - WIB_OFFSET_HOURS, localM, 0, 0);
589
+ nextRunAtEpoch = Math.floor(dt.getTime() / 1000);
590
+ scheduledTimeFinal = `${String(localH).padStart(2, "0")}:${String(localM).padStart(2, "0")}`;
591
+ scheduledDateFinal = dateStr;
592
+ } else {
593
+ // Can't extract time — fall back to UTC parsing
594
+ nextRunAtEpoch = Math.floor(parsedDate.getTime() / 1000);
595
+ scheduledTimeFinal = `${String(parsedDate.getUTCHours()).padStart(2, "0")}:${String(parsedDate.getUTCMinutes()).padStart(2, "0")}`;
596
+ scheduledDateFinal = parsedDate.toISOString().split("T")[0];
597
+ }
577
598
  } else {
578
- // Date only, no time — default to 09:00 UTC
599
+ // Date only, no time — default to 09:00 WIB (= 02:00 UTC)
579
600
  const dateStr = parsedDate.toISOString().split("T")[0];
580
- const dt = new Date(`${dateStr}T09:00:00Z`);
601
+ const dt = new Date(`${dateStr}T00:00:00Z`);
602
+ dt.setUTCHours(9 - WIB_OFFSET_HOURS, 0, 0, 0); // 09:00 WIB = 02:00 UTC
581
603
  nextRunAtEpoch = Math.floor(dt.getTime() / 1000);
582
- scheduledTimeFinal = "09:00";
604
+ scheduledTimeFinal = "09:00"; // Stored as WIB local time
583
605
  scheduledDateFinal = dateStr;
584
606
  }
585
607
  } else {
@@ -611,6 +633,7 @@ async function handleCreateTask(
611
633
  next_run_at: nextRunAtEpoch,
612
634
  run_count: 0,
613
635
  priority: params.priority !== undefined ? params.priority : 1,
636
+ timezone: "Asia/Jakarta",
614
637
  });
615
638
  } catch (schedErr) {
616
639
  // Non-fatal: task was created, just the scheduler event failed