const COLLECTION_RE = /^[a-z0-9][a-z0-9_-]{0,63}$/;
function cleanToken(value) {
    const cleaned = value
        .toLowerCase()
        .replace(/[^a-z0-9_-]+/g, "-")
        .replace(/^-+|-+$/g, "")
        .slice(0, 32);
    return cleaned || "task";
}
export function taskStorageCollection(taskId, suffix) {
    const collection = `task-${cleanToken(taskId)}-${cleanToken(suffix)}`.slice(0, 64);
    return collection.replace(/[-_]+$/g, "") || "task-storage";
}
function policyId(taskId, suffix) {
    return `${cleanToken(taskId)}-${cleanToken(suffix)}`.slice(0, 64);
}
function makePolicy(task, purpose, suffix, policy) {
    const { collection, ...rest } = policy;
    return {
        version: 1,
        id: policyId(task.id, suffix),
        purpose,
        scope: "task",
        collection: collection || taskStorageCollection(task.id, suffix),
        source: "auto",
        ...rest,
    };
}
function sourceText(task) {
    return `${task.name || ""}\n${task.prompt || ""}`.toLowerCase();
}
export function hasStorageIntent(text) {
    const lower = text.toLowerCase();
    return (hasRepeatAvoidanceIntent(lower) ||
        hasRecordIntent(lower) ||
        hasCursorIntent(lower) ||
        hasCounterIntent(lower) ||
        hasObservationIntent(lower) ||
        /\bstore\.(has|put|get|list|count|delete)\b/.test(lower));
}
function hasRepeatAvoidanceIntent(text) {
    return (/不要重复|别重复|避免重复|请勿.{0,12}重复|重复(回复|转发|处理|发布|评论|点赞|访问|quote|reply)|已经(回复|转发|处理|发布|评论|点赞|访问|quote|reply)|已\s*(quote|reply)|处理过|回复过|转发过|发过|访问过/.test(text) ||
        /跳过.{0,12}(已经|处理过|回复过|转发过|发布过|访问过|quote|reply)/.test(text) ||
        /去\s*(quote|reply)|x-(quoted|replied|liked|followed)-history|x-(quoted|replied|liked|followed)/.test(text) ||
        /\b(do not|don't|dont|never|avoid)\s+(repeat|reprocess|handle the same|reply to the same|post the same|visit the same)\b/.test(text) ||
        /\bde-?dupe(?:licate|d)?\b|\bduplicates?\b|\bduplicated\b/.test(text) ||
        /\bskip\s+(already|previously|processed|handled|seen)\b/.test(text) ||
        /\balready\s+(processed|handled|seen|visited|replied|posted|quoted|liked|followed)\b/.test(text));
}
function repeatAvoidanceRoutes(text) {
    const routes = [];
    const standaloneX = /(^|[\s"'“‘({[，,。:：；;、])x($|[\s"'”’)}\]，,。:：；;、])/.test(text) ||
        /\bx\s*(搜索|平台|上|里|推文|帖子|内容|提到|打开|回复)/.test(text) ||
        /(打开|去|在|搜索)\s*x\b/.test(text);
    const xContext = standaloneX ||
        /\bx\.com\b|\btwitter\b|\btweets?\b|\bstatusid\b|\/status\/|推文|转推|引用推|转发推|回复推|x-(quoted|replied|liked|followed)/.test(text);
    const quoteIntent = /\bquote[sd]?\b|\bquoting\b|\brepost(?:ed|ing)?\b|\bretweet(?:ed|ing)?\b|引用|转发|已\s*quote|去\s*quote|x-quoted/.test(text);
    const replyIntent = /\brepl(?:y|ied|ies|ying)\b|回复|评论|已\s*reply|去\s*reply|x-replied/.test(text);
    if (xContext && quoteIntent) {
        routes.push({
            suffix: "x-quoted",
            collection: "x-quoted",
            idHint: "Use the digit status id from the target URL path after /status/ as stableId; do not use the full URL.",
            description: "Use the existing structured history for X quote/repost actions so old migrated records remain effective.",
        });
    }
    if (xContext && replyIntent) {
        routes.push({
            suffix: "x-replied",
            collection: "x-replied",
            idHint: "Use the digit status id from the target URL path after /status/ as stableId; do not use the full URL.",
            description: "Use the existing structured history for X reply/comment actions so old migrated records remain effective.",
        });
    }
    return routes;
}
function hasRecordIntent(text) {
    return (/记录|留存|保存.{0,10}(记录|结果|历史|列表|数据|账号|内容)|追踪|跟踪|日志|写下/.test(text) ||
        /\b(record|track|log|save)\s+(each|every|the|results?|history|items?|accounts?|content|data)\b/.test(text));
}
function hasCursorIntent(text) {
    return (/上次|继续上次|接着上次|从上次|处理到哪|进度/.test(text) ||
        /\b(last processed|resume|cursor|continue from|pick up where)\b/.test(text));
}
function hasCounterIntent(text) {
    return (/每天最多|每小时最多|次数|计数|统计数量|数量统计|上限/.test(text) ||
        /\b(daily|hourly|weekly)?\s*(limit|count|counter|quota|max)\b/.test(text));
}
function hasObservationIntent(text) {
    return (/变化|变动|监控|观察|价格|状态|指标|趋势|更新/.test(text) ||
        /\b(observe|observation|monitor|change|changed|price|status|metric|trend|updated?)\b/.test(text));
}
export function inferStoragePoliciesForTask(task) {
    if (task.storagePolicyMode === "off")
        return [];
    const text = sourceText(task);
    const policies = [];
    if (hasRepeatAvoidanceIntent(text)) {
        const routes = repeatAvoidanceRoutes(text);
        const policyRoutes = routes.length
            ? routes
            : [
                {
                    suffix: "handled-items",
                    collection: taskStorageCollection(task.id, "handled-items"),
                    idHint: "Use the most stable target id available for the item this task acts on.",
                    description: "Avoid acting twice on the same target by checking structured storage before action and recording after verified success.",
                },
            ];
        for (const route of policyRoutes) {
            policies.push(makePolicy(task, "avoid-repeat", route.suffix, {
                collection: route.collection,
                target: "acted-item",
                idStrategy: routes.length ? "entity-key" : "canonical-url-or-link",
                idHint: route.idHint,
                readBefore: [
                    {
                        timing: "before-candidate-action",
                        action: "has",
                        idFrom: routes.length ? "entity-key" : "canonical-url-or-link",
                        onFound: "skip-target",
                    },
                ],
                writeAfter: [
                    {
                        trigger: "after-verified-action",
                        action: "put",
                        idFrom: routes.length ? "entity-key" : "canonical-url-or-link",
                        metaHint: ["url", "title", "author", "action", "observedAt"],
                    },
                ],
                errorBehavior: "fail_closed",
                description: route.description,
            }));
        }
    }
    if (hasRecordIntent(text)) {
        policies.push(makePolicy(task, "record", "records", {
            target: "acted-item",
            idStrategy: "canonical-url-or-link",
            writeAfter: [
                {
                    trigger: "after-verified-action",
                    action: "put",
                    idFrom: "canonical-url-or-link",
                    valueHint: "Short human-readable summary when useful.",
                    metaHint: ["url", "title", "author", "content", "action", "observedAt"],
                },
            ],
            errorBehavior: "continue_with_warning",
            description: "Record structured facts produced by this task so later runs can inspect them without parsing freeform memory.",
        }));
    }
    if (hasCursorIntent(text)) {
        policies.push(makePolicy(task, "cursor", "cursor", {
            target: "cursor",
            idStrategy: "entity-key",
            readBefore: [
                {
                    timing: "before-run",
                    action: "get",
                    idFrom: "entity-key",
                    bindAs: "previousProgress",
                    onFound: "continue",
                },
            ],
            writeAfter: [
                {
                    trigger: "after-progress",
                    action: "put",
                    idFrom: "entity-key",
                    valueHint: "Latest stable progress marker.",
                    metaHint: ["url", "label", "observedAt"],
                },
            ],
            errorBehavior: "continue_with_warning",
            description: "Read and update a stable progress marker so the next run can continue from the prior state.",
        }));
    }
    if (hasCounterIntent(text)) {
        policies.push(makePolicy(task, "counter", "counters", {
            target: "date-bucket",
            idStrategy: "date-bucket",
            readBefore: [
                {
                    timing: "before-run",
                    action: "get",
                    idFrom: "date-bucket",
                    bindAs: "currentBucket",
                    onFound: "continue",
                },
            ],
            writeAfter: [
                {
                    trigger: "after-verified-action",
                    action: "put",
                    idFrom: "date-bucket",
                    valueHint: "Updated count or compact JSON string for the current time bucket.",
                    metaHint: ["bucket", "count", "updatedAt"],
                },
            ],
            errorBehavior: "continue_with_warning",
            description: "Track counts or quotas in date-based buckets instead of relying on conversational history.",
        }));
    }
    if (hasObservationIntent(text)) {
        policies.push(makePolicy(task, "observation", "observations", {
            target: "observed-entity",
            idStrategy: "entity-key",
            readBefore: [
                {
                    timing: "before-observation",
                    action: "get",
                    idFrom: "entity-key",
                    bindAs: "previousObservation",
                    onFound: "compare-before-writing",
                },
            ],
            writeAfter: [
                {
                    trigger: "after-observation",
                    action: "put",
                    idFrom: "entity-key",
                    valueHint: "Short current value when useful.",
                    metaHint: ["name", "url", "value", "previousValue", "observedAt"],
                },
            ],
            errorBehavior: "continue_with_warning",
            description: "Store structured observations so later runs can compare current values with prior values.",
        }));
    }
    return policies;
}
export function normalizeStoragePolicies(policies, taskId) {
    if (!Array.isArray(policies))
        return [];
    const normalized = [];
    for (const item of policies) {
        if (!item || typeof item !== "object")
            continue;
        const raw = item;
        if (raw.version !== 1)
            continue;
        if (!raw.purpose || !raw.collection || !raw.idStrategy || !raw.target)
            continue;
        const collection = COLLECTION_RE.test(raw.collection)
            ? raw.collection
            : taskStorageCollection(taskId, String(raw.purpose));
        normalized.push({
            version: 1,
            id: raw.id || policyId(taskId, raw.purpose),
            purpose: raw.purpose,
            scope: raw.scope === "workspace" ? "workspace" : "task",
            collection,
            target: raw.target,
            idStrategy: raw.idStrategy,
            idHint: typeof raw.idHint === "string" ? raw.idHint : undefined,
            readBefore: Array.isArray(raw.readBefore) ? raw.readBefore : undefined,
            writeAfter: Array.isArray(raw.writeAfter) ? raw.writeAfter : undefined,
            errorBehavior: raw.errorBehavior === "continue_with_warning"
                ? "continue_with_warning"
                : "fail_closed",
            source: raw.source === "manual" || raw.source === "migration" ? raw.source : "auto",
            description: raw.description,
        });
    }
    return normalized;
}
export function mergeAutoStoragePolicies(task) {
    if (task.storagePolicyMode === "off")
        return [];
    const existing = normalizeStoragePolicies(task.storagePolicies, task.id);
    const retained = existing.filter((p) => p.source !== "auto");
    const inferred = inferStoragePoliciesForTask(task);
    return uniquePolicies([...retained, ...inferred]);
}
function uniquePolicies(policies) {
    const seen = new Set();
    const result = [];
    for (const policy of policies) {
        const key = `${policy.purpose}:${policy.collection}:${policy.target}`;
        if (seen.has(key))
            continue;
        seen.add(key);
        result.push(policy);
    }
    return result;
}
function describeIdStrategy(strategy) {
    switch (strategy) {
        case "canonical-url-or-link":
            return "Use the most stable target identifier available: canonical URL, candidate link href, current page URL, then a compact content hash.";
        case "entity-key":
            return "Use a stable entity key from the page, such as normalized name, URL, item id, or a compact content hash.";
        case "date-bucket":
            return "Use a stable date/time bucket such as YYYY-MM-DD or YYYY-MM-DDTHH, based on the task's intended quota window.";
        case "content-hash":
            return "Use a compact hash of normalized content when no URL or entity key is available.";
    }
}
function describeReadRule(rule, collection, strategy) {
    const id = rule.idFrom || strategy;
    const action = rule.action === "has"
        ? `Call store.has("${collection}", stableId).`
        : rule.action === "get"
            ? `Call store.get("${collection}", stableId).`
            : rule.action === "count"
                ? `Call store.count("${collection}").`
                : `Call store.list("${collection}", limit).`;
    const found = rule.onFound === "skip-target"
        ? "If it is already present, skip that target and choose another."
        : rule.onFound === "compare-before-writing"
            ? "If a prior entry exists, compare it with the current observation before deciding what to write or report."
            : "Use the result as structured state for this run.";
    return `- ${rule.timing}: derive stableId via ${id}. ${action} ${found}`;
}
function describeWriteRule(rule, collection, strategy) {
    const meta = rule.metaHint?.length
        ? ` Include useful meta fields when available: ${rule.metaHint.join(", ")}.`
        : "";
    const value = rule.valueHint ? ` Value hint: ${rule.valueHint}` : "";
    return `- ${rule.trigger}: derive stableId via ${rule.idFrom || strategy}. Call store.put("${collection}", stableId, value?, meta?).${value}${meta}`;
}
export function renderStoragePolicySection(policies) {
    if (policies.length === 0)
        return "";
    const lines = [
        "## Runtime Storage Policies",
        "The task has structured storage policies generated from the user's natural-language request. The user does not need to know collection names or tool syntax.",
        "Use these policies whenever the task needs cross-run structured state. They override any task text that suggests manual history files or freeform memory for structured state.",
        "If a store operation returns an error or rejection and the policy says fail_closed, stop the task and report the storage problem.",
    ];
    for (const policy of policies) {
        lines.push("", `Policy ${policy.id} (${policy.purpose})`, `- Collection: "${policy.collection}"`, `- Target: ${policy.target}`, `- Stable id: ${describeIdStrategy(policy.idStrategy)}`, `- Error behavior: ${policy.errorBehavior}`);
        if (policy.idHint) {
            lines.push(`- Stable id hint: ${policy.idHint}`);
        }
        if (policy.description) {
            lines.push(`- Purpose: ${policy.description}`);
        }
        if (policy.readBefore?.length) {
            lines.push("- Read rules:");
            for (const rule of policy.readBefore) {
                lines.push(describeReadRule(rule, policy.collection, policy.idStrategy));
            }
        }
        if (policy.writeAfter?.length) {
            lines.push("- Write rules:");
            for (const rule of policy.writeAfter) {
                lines.push(describeWriteRule(rule, policy.collection, policy.idStrategy));
            }
        }
    }
    return lines.join("\n");
}
export class StoragePolicyRuntimeTracker {
    policies;
    calls = new Map();
    reminderSent = false;
    constructor(policies) {
        this.policies = policies;
    }
    recordStoreCall(action, collection) {
        if (typeof collection !== "string")
            return;
        const actions = this.calls.get(collection) ?? new Set();
        actions.add(action);
        this.calls.set(collection, actions);
    }
    completionReminder() {
        if (this.reminderSent || this.policies.length === 0)
            return null;
        const incomplete = this.policies.filter((policy) => {
            const actions = this.calls.get(policy.collection) ?? new Set();
            const readOk = (policy.readBefore ?? []).every((rule) => actions.has(rule.action));
            const writeOk = (policy.writeAfter ?? []).every((rule) => actions.has(rule.action));
            return !readOk || !writeOk;
        });
        if (incomplete.length > 0) {
            this.reminderSent = true;
            const missing = incomplete
                .map((policy) => {
                const actions = this.calls.get(policy.collection) ?? new Set();
                const missingReads = (policy.readBefore ?? [])
                    .map((rule) => rule.action)
                    .filter((action) => !actions.has(action));
                const missingWrites = (policy.writeAfter ?? [])
                    .map((rule) => rule.action)
                    .filter((action) => !actions.has(action));
                return `${policy.id} (${policy.collection}): missing ${[
                    ...missingReads,
                    ...missingWrites,
                ].join(", ")}`;
            })
                .join("\n");
            return [
                "Before finishing, check the Runtime Storage Policies.",
                missing,
                "If this run acted on, observed, counted, or advanced any target covered by those policies, perform the required store calls now.",
                "If no storage call is appropriate because there was no matching target or no state changed, say that explicitly in the final answer.",
            ].join("\n");
        }
        return null;
    }
}
//# sourceMappingURL=storage-policy.js.map