@mupt-ai/dari-gateway 0.2.0 → 0.2.1
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/dist/bun.d.ts +2 -0
- package/dist/bun.js +4 -0
- package/dist/context/eval_catalog.d.ts +3 -0
- package/dist/context/eval_catalog.js +7 -0
- package/dist/context/interaction_fingerprint.d.ts +27 -0
- package/dist/context/interaction_fingerprint.js +519 -0
- package/dist/context/interaction_fingerprint_format.d.ts +42 -0
- package/dist/context/interaction_fingerprint_format.js +113 -0
- package/dist/context/interaction_lease_depth.d.ts +2 -0
- package/dist/context/interaction_lease_depth.js +110 -0
- package/dist/context/lease_history.d.ts +7 -0
- package/dist/context/lease_history.js +158 -0
- package/dist/context/pricing.d.ts +4 -0
- package/dist/context/pricing.js +23 -0
- package/dist/context/provider_prompt_accounting.d.ts +14 -0
- package/dist/context/provider_prompt_accounting.js +187 -0
- package/dist/context/routing_context.d.ts +40 -0
- package/dist/context/routing_context.js +211 -0
- package/dist/http/auth.d.ts +2 -0
- package/dist/http/auth.js +35 -0
- package/dist/http/errors.d.ts +19 -0
- package/dist/http/errors.js +89 -0
- package/dist/http/handler.d.ts +12 -0
- package/dist/http/handler.js +81 -0
- package/dist/http/http2_server.d.ts +6 -0
- package/dist/http/http2_server.js +103 -0
- package/dist/http/serve.d.ts +20 -0
- package/dist/http/serve.js +78 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +8 -0
- package/dist/ingress/anthropic_messages.d.ts +3 -0
- package/dist/ingress/anthropic_messages.js +122 -0
- package/dist/ingress/anthropic_models.d.ts +1 -0
- package/dist/ingress/anthropic_models.js +23 -0
- package/dist/ingress/chat_completions.d.ts +2 -0
- package/dist/ingress/chat_completions.js +37 -0
- package/dist/ingress/claude_code/request_context.d.ts +2 -0
- package/dist/ingress/claude_code/request_context.js +31 -0
- package/dist/ingress/codex.d.ts +3 -0
- package/dist/ingress/codex.js +65 -0
- package/dist/ingress/codex_models.d.ts +2 -0
- package/dist/ingress/codex_models.js +89 -0
- package/dist/ingress/json.d.ts +1 -0
- package/dist/ingress/json.js +95 -0
- package/dist/ingress/models.d.ts +1 -0
- package/dist/ingress/models.js +53 -0
- package/dist/interaction/capabilities.d.ts +3 -0
- package/dist/interaction/capabilities.js +24 -0
- package/dist/interaction/image.d.ts +4 -0
- package/dist/interaction/image.js +13 -0
- package/dist/interaction/portable_reasoning_state.d.ts +4 -0
- package/dist/interaction/portable_reasoning_state.js +18 -0
- package/dist/interaction/provider_continuation_state.d.ts +6 -0
- package/dist/interaction/provider_continuation_state.js +27 -0
- package/dist/interaction/tool_identity.d.ts +10 -0
- package/dist/interaction/tool_identity.js +41 -0
- package/dist/interaction/types.d.ts +203 -0
- package/dist/interaction/types.js +5 -0
- package/dist/models/context_limits.d.ts +2 -0
- package/dist/models/context_limits.js +7 -0
- package/dist/models/execution_provider.d.ts +8 -0
- package/dist/models/execution_provider.js +13 -0
- package/dist/models/gateways.d.ts +11 -0
- package/dist/models/gateways.js +45 -0
- package/dist/models/ids.d.ts +1 -0
- package/dist/models/ids.js +15 -0
- package/dist/models/provider_defaults.d.ts +4 -0
- package/dist/models/provider_defaults.js +41 -0
- package/dist/models/runtime_overrides.d.ts +4 -0
- package/dist/models/runtime_overrides.js +209 -0
- package/dist/observability/errors.d.ts +8 -0
- package/dist/observability/errors.js +11 -0
- package/dist/platform/platform.d.ts +152 -0
- package/dist/platform/platform.js +11 -0
- package/dist/platform/routing_contract.d.ts +2 -0
- package/dist/platform/routing_contract.js +5 -0
- package/dist/protocol/input/anthropic_messages.d.ts +2 -0
- package/dist/protocol/input/anthropic_messages.js +494 -0
- package/dist/protocol/input/chat_completions.d.ts +4 -0
- package/dist/protocol/input/chat_completions.js +212 -0
- package/dist/protocol/input/chat_completions_validation.d.ts +2 -0
- package/dist/protocol/input/chat_completions_validation.js +257 -0
- package/dist/protocol/input/openai_responses.d.ts +2 -0
- package/dist/protocol/input/openai_responses.js +666 -0
- package/dist/protocol/input/router_model.d.ts +7 -0
- package/dist/protocol/input/router_model.js +43 -0
- package/dist/protocol/output/anthropic_messages.d.ts +10 -0
- package/dist/protocol/output/anthropic_messages.js +15 -0
- package/dist/protocol/output/anthropic_messages_output.d.ts +10 -0
- package/dist/protocol/output/anthropic_messages_output.js +124 -0
- package/dist/protocol/output/anthropic_messages_stream.d.ts +16 -0
- package/dist/protocol/output/anthropic_messages_stream.js +267 -0
- package/dist/protocol/output/chat_completions.d.ts +29 -0
- package/dist/protocol/output/chat_completions.js +406 -0
- package/dist/protocol/output/chat_reasoning_detail.d.ts +2 -0
- package/dist/protocol/output/chat_reasoning_detail.js +13 -0
- package/dist/protocol/output/delivered_stream_snapshot.d.ts +10 -0
- package/dist/protocol/output/delivered_stream_snapshot.js +31 -0
- package/dist/protocol/output/openai_responses.d.ts +10 -0
- package/dist/protocol/output/openai_responses.js +14 -0
- package/dist/protocol/output/openai_responses_output.d.ts +14 -0
- package/dist/protocol/output/openai_responses_output.js +125 -0
- package/dist/protocol/output/openai_responses_stream.d.ts +18 -0
- package/dist/protocol/output/openai_responses_stream.js +546 -0
- package/dist/protocol/output/sse_stream.d.ts +31 -0
- package/dist/protocol/output/sse_stream.js +67 -0
- package/dist/providers/anthropic_safeguards.d.ts +11 -0
- package/dist/providers/anthropic_safeguards.js +81 -0
- package/dist/providers/aws_assume_role.d.ts +17 -0
- package/dist/providers/aws_assume_role.js +107 -0
- package/dist/providers/cancel_estimate.d.ts +43 -0
- package/dist/providers/cancel_estimate.js +89 -0
- package/dist/providers/execution.d.ts +72 -0
- package/dist/providers/execution.js +491 -0
- package/dist/providers/model_capabilities.d.ts +11 -0
- package/dist/providers/model_capabilities.js +29 -0
- package/dist/providers/pi.d.ts +65 -0
- package/dist/providers/pi.js +319 -0
- package/dist/providers/pi_context.d.ts +7 -0
- package/dist/providers/pi_context.js +405 -0
- package/dist/providers/pi_options.d.ts +11 -0
- package/dist/providers/pi_options.js +398 -0
- package/dist/providers/pi_output.d.ts +2 -0
- package/dist/providers/pi_output.js +453 -0
- package/dist/providers/provider_attempt_runner.d.ts +55 -0
- package/dist/providers/provider_attempt_runner.js +90 -0
- package/dist/providers/provider_call.d.ts +77 -0
- package/dist/providers/provider_call.js +292 -0
- package/dist/providers/provider_reported_cost.d.ts +10 -0
- package/dist/providers/provider_reported_cost.js +123 -0
- package/dist/providers/reasoning.d.ts +12 -0
- package/dist/providers/reasoning.js +30 -0
- package/dist/providers/response_capture.d.ts +23 -0
- package/dist/providers/response_capture.js +47 -0
- package/dist/providers/stream_finalization.d.ts +46 -0
- package/dist/providers/stream_finalization.js +165 -0
- package/dist/providers/subscription_limits.d.ts +31 -0
- package/dist/providers/subscription_limits.js +163 -0
- package/dist/providers/subscription_usage.d.ts +10 -0
- package/dist/providers/subscription_usage.js +79 -0
- package/dist/runtime/claude_code/auxiliary_routing.d.ts +3 -0
- package/dist/runtime/claude_code/auxiliary_routing.js +60 -0
- package/dist/runtime/customer_disconnect.d.ts +20 -0
- package/dist/runtime/customer_disconnect.js +49 -0
- package/dist/runtime/explicit_model.d.ts +4 -0
- package/dist/runtime/explicit_model.js +53 -0
- package/dist/runtime/harness_identity/conversation.d.ts +33 -0
- package/dist/runtime/harness_identity/conversation.js +39 -0
- package/dist/runtime/harness_identity/extract.d.ts +8 -0
- package/dist/runtime/harness_identity/extract.js +214 -0
- package/dist/runtime/harness_identity/index.d.ts +2 -0
- package/dist/runtime/harness_identity/index.js +2 -0
- package/dist/runtime/prepare_routed_completion.d.ts +33 -0
- package/dist/runtime/prepare_routed_completion.js +167 -0
- package/dist/runtime/provider_stream_lifecycle.d.ts +31 -0
- package/dist/runtime/provider_stream_lifecycle.js +88 -0
- package/dist/runtime/provider_stream_outcome.d.ts +23 -0
- package/dist/runtime/provider_stream_outcome.js +55 -0
- package/dist/runtime/request_tags.d.ts +7 -0
- package/dist/runtime/request_tags.js +33 -0
- package/dist/runtime/routed_completion.d.ts +17 -0
- package/dist/runtime/routed_completion.js +80 -0
- package/dist/runtime/routed_protocol.d.ts +30 -0
- package/dist/runtime/routed_protocol.js +1 -0
- package/dist/runtime/routed_runtime.d.ts +40 -0
- package/dist/runtime/routed_runtime.js +535 -0
- package/dist/runtime/routing_lease.d.ts +26 -0
- package/dist/runtime/routing_lease.js +210 -0
- package/dist/runtime/runtime_reporter.d.ts +71 -0
- package/dist/runtime/runtime_reporter.js +35 -0
- package/dist/self_host/config.d.ts +46 -0
- package/dist/self_host/config.js +105 -0
- package/dist/self_host/main.d.ts +16 -0
- package/dist/self_host/main.js +95 -0
- package/dist/self_host/platform.d.ts +13 -0
- package/dist/self_host/platform.js +50 -0
- package/dist/self_host/prefix_store.d.ts +46 -0
- package/dist/self_host/prefix_store.js +261 -0
- package/dist/self_host/resolve.d.ts +28 -0
- package/dist/self_host/resolve.js +171 -0
- package/dist/self_host/subscriptions/access.d.ts +10 -0
- package/dist/self_host/subscriptions/access.js +37 -0
- package/dist/self_host/subscriptions/http.d.ts +15 -0
- package/dist/self_host/subscriptions/http.js +187 -0
- package/dist/self_host/subscriptions/index.d.ts +24 -0
- package/dist/self_host/subscriptions/index.js +24 -0
- package/dist/self_host/subscriptions/oauth.d.ts +55 -0
- package/dist/self_host/subscriptions/oauth.js +281 -0
- package/dist/self_host/subscriptions/store.d.ts +78 -0
- package/dist/self_host/subscriptions/store.js +235 -0
- package/dist/strategies/auto_routing_selector.d.ts +12 -0
- package/dist/strategies/auto_routing_selector.js +61 -0
- package/dist/strategies/eval_relevance.d.ts +17 -0
- package/dist/strategies/eval_relevance.js +32 -0
- package/dist/strategies/fallback_decision.d.ts +8 -0
- package/dist/strategies/fallback_decision.js +17 -0
- package/dist/strategies/priority_selector.d.ts +7 -0
- package/dist/strategies/priority_selector.js +37 -0
- package/dist/strategies/route_decision.d.ts +22 -0
- package/dist/strategies/route_decision.js +65 -0
- package/dist/strategies/routing.d.ts +74 -0
- package/dist/strategies/routing.js +280 -0
- package/dist/strategies/selector_projection.d.ts +15 -0
- package/dist/strategies/selector_projection.js +161 -0
- package/dist/telemetry/background.d.ts +7 -0
- package/dist/telemetry/background.js +33 -0
- package/dist/telemetry/benchmark_replay.d.ts +37 -0
- package/dist/telemetry/benchmark_replay.js +108 -0
- package/dist/telemetry/billing_finalization.d.ts +64 -0
- package/dist/telemetry/billing_finalization.js +96 -0
- package/dist/telemetry/ingest_values.d.ts +4 -0
- package/dist/telemetry/ingest_values.js +12 -0
- package/dist/telemetry/model_cost.d.ts +3 -0
- package/dist/telemetry/model_cost.js +15 -0
- package/dist/telemetry/prefix_state.d.ts +47 -0
- package/dist/telemetry/prefix_state.js +116 -0
- package/dist/telemetry/report.d.ts +88 -0
- package/dist/telemetry/report.js +621 -0
- package/dist/telemetry/requests/interaction_observability.d.ts +45 -0
- package/dist/telemetry/requests/interaction_observability.js +180 -0
- package/dist/telemetry/requests/retained_interaction_request.d.ts +8 -0
- package/dist/telemetry/requests/retained_interaction_request.js +10 -0
- package/dist/telemetry/scrub.d.ts +2 -0
- package/dist/telemetry/scrub.js +31 -0
- package/dist/telemetry/skills/claude/catalog.d.ts +6 -0
- package/dist/telemetry/skills/claude/catalog.js +99 -0
- package/dist/telemetry/skills/claude/file_reads.d.ts +4 -0
- package/dist/telemetry/skills/claude/file_reads.js +110 -0
- package/dist/telemetry/skills/claude/observations.d.ts +3 -0
- package/dist/telemetry/skills/claude/observations.js +366 -0
- package/dist/telemetry/skills/codex/catalog.d.ts +11 -0
- package/dist/telemetry/skills/codex/catalog.js +138 -0
- package/dist/telemetry/skills/codex/exec_file_reads.d.ts +9 -0
- package/dist/telemetry/skills/codex/exec_file_reads.js +36 -0
- package/dist/telemetry/skills/codex/explicit.d.ts +3 -0
- package/dist/telemetry/skills/codex/explicit.js +90 -0
- package/dist/telemetry/skills/codex/file_reads.d.ts +4 -0
- package/dist/telemetry/skills/codex/file_reads.js +37 -0
- package/dist/telemetry/skills/codex/identity.d.ts +2 -0
- package/dist/telemetry/skills/codex/identity.js +20 -0
- package/dist/telemetry/skills/observations.d.ts +21 -0
- package/dist/telemetry/skills/observations.js +41 -0
- package/dist/telemetry/skills/paths.d.ts +11 -0
- package/dist/telemetry/skills/paths.js +61 -0
- package/dist/telemetry/skills/pi.d.ts +6 -0
- package/dist/telemetry/skills/pi.js +223 -0
- package/dist/telemetry/skills/shell_file_reads.d.ts +7 -0
- package/dist/telemetry/skills/shell_file_reads.js +226 -0
- package/dist/telemetry/skills/shell_words.d.ts +13 -0
- package/dist/telemetry/skills/shell_words.js +189 -0
- package/dist/telemetry/tools/input.d.ts +2 -0
- package/dist/telemetry/tools/input.js +14 -0
- package/dist/telemetry/tools/observations.d.ts +29 -0
- package/dist/telemetry/tools/observations.js +247 -0
- package/dist/telemetry/usage.d.ts +57 -0
- package/dist/telemetry/usage.js +136 -0
- package/dist/types.d.ts +94 -0
- package/dist/types.js +1 -0
- package/package.json +21 -6
- package/src/bun.ts +2 -2
- package/src/context/eval_catalog.ts +1 -1
- package/src/context/interaction_fingerprint.ts +9 -9
- package/src/context/interaction_fingerprint_format.ts +1 -1
- package/src/context/interaction_lease_depth.ts +1 -1
- package/src/context/pricing.ts +1 -1
- package/src/context/provider_prompt_accounting.ts +5 -5
- package/src/context/routing_context.ts +16 -16
- package/src/http/auth.ts +1 -1
- package/src/http/handler.ts +8 -8
- package/src/http/serve.ts +3 -3
- package/src/index.ts +6 -6
- package/src/ingress/anthropic_messages.ts +12 -12
- package/src/ingress/anthropic_models.ts +1 -1
- package/src/ingress/chat_completions.ts +9 -9
- package/src/ingress/claude_code/request_context.ts +2 -2
- package/src/ingress/codex.ts +10 -10
- package/src/ingress/codex_models.ts +4 -4
- package/src/ingress/json.ts +1 -1
- package/src/ingress/models.ts +9 -9
- package/src/interaction/capabilities.ts +1 -1
- package/src/interaction/portable_reasoning_state.ts +1 -1
- package/src/interaction/provider_continuation_state.ts +1 -1
- package/src/interaction/tool_identity.ts +2 -2
- package/src/models/context_limits.ts +1 -1
- package/src/models/execution_provider.ts +1 -1
- package/src/models/ids.ts +1 -1
- package/src/models/provider_defaults.ts +1 -1
- package/src/models/runtime_overrides.ts +1 -1
- package/src/observability/errors.ts +2 -2
- package/src/platform/platform.ts +4 -4
- package/src/protocol/input/anthropic_messages.ts +7 -7
- package/src/protocol/input/chat_completions.ts +5 -5
- package/src/protocol/input/chat_completions_validation.ts +4 -4
- package/src/protocol/input/openai_responses.ts +6 -6
- package/src/protocol/output/anthropic_messages.ts +3 -3
- package/src/protocol/output/anthropic_messages_output.ts +6 -4
- package/src/protocol/output/anthropic_messages_stream.ts +7 -7
- package/src/protocol/output/chat_completions.ts +13 -13
- package/src/protocol/output/chat_reasoning_detail.ts +1 -1
- package/src/protocol/output/openai_responses.ts +3 -3
- package/src/protocol/output/openai_responses_output.ts +4 -2
- package/src/protocol/output/openai_responses_stream.ts +6 -6
- package/src/protocol/output/sse_stream.ts +3 -3
- package/src/providers/anthropic_safeguards.ts +2 -2
- package/src/providers/aws_assume_role.ts +2 -2
- package/src/providers/cancel_estimate.ts +1 -1
- package/src/providers/execution.ts +17 -17
- package/src/providers/pi.ts +14 -14
- package/src/providers/pi_context.ts +6 -6
- package/src/providers/pi_options.ts +10 -10
- package/src/providers/pi_output.ts +5 -5
- package/src/providers/provider_attempt_runner.ts +1 -1
- package/src/providers/provider_call.ts +14 -14
- package/src/providers/provider_reported_cost.ts +3 -3
- package/src/providers/reasoning.ts +2 -2
- package/src/providers/stream_finalization.ts +9 -9
- package/src/providers/subscription_limits.ts +3 -3
- package/src/runtime/claude_code/auxiliary_routing.ts +5 -5
- package/src/runtime/customer_disconnect.ts +1 -1
- package/src/runtime/explicit_model.ts +4 -4
- package/src/runtime/harness_identity/conversation.ts +1 -1
- package/src/runtime/harness_identity/extract.ts +3 -3
- package/src/runtime/harness_identity/index.ts +2 -2
- package/src/runtime/prepare_routed_completion.ts +13 -13
- package/src/runtime/provider_stream_lifecycle.ts +1 -1
- package/src/runtime/provider_stream_outcome.ts +2 -2
- package/src/runtime/request_tags.ts +2 -2
- package/src/runtime/routed_completion.ts +11 -11
- package/src/runtime/routed_protocol.ts +2 -2
- package/src/runtime/routed_runtime.ts +20 -19
- package/src/runtime/routing_lease.ts +7 -7
- package/src/runtime/runtime_reporter.ts +3 -3
- package/src/self_host/main.ts +12 -12
- package/src/self_host/platform.ts +5 -5
- package/src/self_host/prefix_store.ts +2 -2
- package/src/self_host/resolve.ts +6 -6
- package/src/self_host/subscriptions/access.ts +2 -2
- package/src/self_host/subscriptions/http.ts +2 -2
- package/src/self_host/subscriptions/index.ts +4 -4
- package/src/self_host/subscriptions/store.ts +1 -1
- package/src/strategies/auto_routing_selector.ts +2 -2
- package/src/strategies/eval_relevance.ts +3 -3
- package/src/strategies/priority_selector.ts +1 -1
- package/src/strategies/route_decision.ts +7 -7
- package/src/strategies/routing.ts +10 -10
- package/src/strategies/selector_projection.ts +2 -2
- package/src/telemetry/billing_finalization.ts +3 -3
- package/src/telemetry/model_cost.ts +1 -1
- package/src/telemetry/prefix_state.ts +4 -4
- package/src/telemetry/report.ts +21 -21
- package/src/telemetry/requests/interaction_observability.ts +1 -1
- package/src/telemetry/requests/retained_interaction_request.ts +1 -1
- package/src/telemetry/skills/claude/catalog.ts +1 -1
- package/src/telemetry/skills/claude/file_reads.ts +6 -6
- package/src/telemetry/skills/claude/observations.ts +3 -3
- package/src/telemetry/skills/codex/catalog.ts +3 -3
- package/src/telemetry/skills/codex/exec_file_reads.ts +4 -4
- package/src/telemetry/skills/codex/explicit.ts +3 -3
- package/src/telemetry/skills/codex/file_reads.ts +5 -5
- package/src/telemetry/skills/observations.ts +7 -7
- package/src/telemetry/skills/pi.ts +3 -3
- package/src/telemetry/skills/shell_file_reads.ts +2 -2
- package/src/telemetry/skills/shell_words.ts +1 -1
- package/src/telemetry/tools/input.ts +1 -1
- package/src/telemetry/tools/observations.ts +9 -9
- package/src/telemetry/usage.ts +5 -5
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
import { isRecord } from "@mupt-ai/dari-router/policy-engine";
|
|
2
|
+
import { claudeAvailableSkillNames, claudeSkillCapabilityId, normalizedClaudeSkillName, } from "./catalog.js";
|
|
3
|
+
const MAX_SKILL_PATH_CHARS = 16 * 1024;
|
|
4
|
+
const UNSAFE_DISPLAY_CHARACTERS = /[\u0000-\u001f\u007f-\u009f\u202a-\u202e\u2066-\u2069]/u;
|
|
5
|
+
const BUDGET_REMINDER_PREFIX = "\n\n<system-reminder>\nUSD budget: ";
|
|
6
|
+
const BUDGET_REMINDER_SUFFIX = "\n</system-reminder>";
|
|
7
|
+
export function claudeSkillObservations(request, limit) {
|
|
8
|
+
return request.requestPurpose === "tool_continuation"
|
|
9
|
+
? confirmedSkillObservations(request, limit)
|
|
10
|
+
: request.requestPurpose === undefined
|
|
11
|
+
? directSkillObservations(request, limit)
|
|
12
|
+
: [];
|
|
13
|
+
}
|
|
14
|
+
function directSkillObservations(request, limit) {
|
|
15
|
+
const message = latestUserMessage(request);
|
|
16
|
+
if (message === undefined)
|
|
17
|
+
return [];
|
|
18
|
+
const availableSkills = claudeAvailableSkillNames(request);
|
|
19
|
+
return directSkillNames(message, availableSkills)
|
|
20
|
+
.slice(0, limit)
|
|
21
|
+
.map((name) => observation(name, null, "skill_command_expansion"));
|
|
22
|
+
}
|
|
23
|
+
function confirmedSkillObservations(request, limit) {
|
|
24
|
+
const confirmedGroups = [];
|
|
25
|
+
let confirmedCount = 0;
|
|
26
|
+
const skillCallsByResultIndex = callsByResultIndex(request);
|
|
27
|
+
const availableSkills = claudeAvailableSkillNames(request);
|
|
28
|
+
let index = request.items.length - 1;
|
|
29
|
+
while (index >= 0) {
|
|
30
|
+
while (index >= 0 && isBudgetStatusMessage(request.items[index]))
|
|
31
|
+
index -= 1;
|
|
32
|
+
if (index < 0)
|
|
33
|
+
break;
|
|
34
|
+
const expansionEvidence = skillExpansionEvidence(request.items[index]);
|
|
35
|
+
let resultStart = expansionEvidence === undefined ? index + 1 : index;
|
|
36
|
+
while (resultStart > 0) {
|
|
37
|
+
const item = request.items[resultStart - 1];
|
|
38
|
+
if (item.type !== "tool_result")
|
|
39
|
+
break;
|
|
40
|
+
resultStart -= 1;
|
|
41
|
+
}
|
|
42
|
+
const resultEnd = expansionEvidence === undefined ? index + 1 : index;
|
|
43
|
+
if (resultStart === resultEnd)
|
|
44
|
+
break;
|
|
45
|
+
const remaining = limit - confirmedCount;
|
|
46
|
+
const group = [];
|
|
47
|
+
let inlineCount = 0;
|
|
48
|
+
let forkedCount = 0;
|
|
49
|
+
for (let resultIndex = resultStart; resultIndex < resultEnd; resultIndex += 1) {
|
|
50
|
+
const item = request.items[resultIndex];
|
|
51
|
+
if (item.type !== "tool_result")
|
|
52
|
+
break;
|
|
53
|
+
const call = skillCallsByResultIndex.get(resultIndex);
|
|
54
|
+
const output = toolResultText(item);
|
|
55
|
+
if (call === undefined || item.isError === true || output === undefined)
|
|
56
|
+
continue;
|
|
57
|
+
if (isInlineSkillResult(output, call.name)) {
|
|
58
|
+
if (expansionEvidence !== undefined) {
|
|
59
|
+
const evidence = expansionEvidence[inlineCount];
|
|
60
|
+
if (evidence?.name !== undefined && evidence.name !== call.name) {
|
|
61
|
+
return orderedGroups(confirmedGroups);
|
|
62
|
+
}
|
|
63
|
+
const redundantDirectInvocation = evidence?.name !== undefined
|
|
64
|
+
&& latestUserBeforeCallIncludesDirectSkill(request, call.callIndex, call.name, availableSkills);
|
|
65
|
+
if (!redundantDirectInvocation && group.length < remaining) {
|
|
66
|
+
group.push(observation(call.name, item.callId, "skill_tool_activation"));
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
inlineCount += 1;
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
if (isForkedSkillResult(output, call.name)) {
|
|
73
|
+
forkedCount += 1;
|
|
74
|
+
if (group.length < remaining) {
|
|
75
|
+
group.push(observation(call.name, item.callId, "skill_tool_activation"));
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (expansionEvidence === undefined) {
|
|
80
|
+
if (forkedCount === 0)
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
else if (inlineCount !== expansionEvidence.length) {
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
confirmedGroups.push(group);
|
|
87
|
+
confirmedCount += group.length;
|
|
88
|
+
if (confirmedCount === limit)
|
|
89
|
+
break;
|
|
90
|
+
index = resultStart - 1;
|
|
91
|
+
}
|
|
92
|
+
return orderedGroups(confirmedGroups);
|
|
93
|
+
}
|
|
94
|
+
function isBudgetStatusMessage(item) {
|
|
95
|
+
if (item.type !== "message"
|
|
96
|
+
|| (item.role !== "system" && item.role !== "developer")
|
|
97
|
+
|| item.content.length !== 1
|
|
98
|
+
|| item.content[0].type !== "text") {
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
const lines = item.content[0].text.split("\n\n");
|
|
102
|
+
return lines.length > 0 && lines.every((line) => {
|
|
103
|
+
if (!line.startsWith("USD budget: "))
|
|
104
|
+
return false;
|
|
105
|
+
const value = line.slice("USD budget: ".length);
|
|
106
|
+
return value.length > 0 && value.length <= 256 && !/[\n<>]/u.test(value);
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
function orderedGroups(groups) {
|
|
110
|
+
groups.reverse();
|
|
111
|
+
return groups.flat();
|
|
112
|
+
}
|
|
113
|
+
function skillCallName(item) {
|
|
114
|
+
const input = item.input;
|
|
115
|
+
if (item.kind !== "function"
|
|
116
|
+
|| item.namespace !== undefined
|
|
117
|
+
|| item.name !== "Skill"
|
|
118
|
+
|| !isRecord(input)) {
|
|
119
|
+
return undefined;
|
|
120
|
+
}
|
|
121
|
+
const inputKeys = Object.keys(input);
|
|
122
|
+
if (inputKeys.some((key) => key !== "skill" && key !== "args")
|
|
123
|
+
|| (input.args !== undefined && typeof input.args !== "string")) {
|
|
124
|
+
return undefined;
|
|
125
|
+
}
|
|
126
|
+
return normalizedClaudeSkillName(input.skill);
|
|
127
|
+
}
|
|
128
|
+
function callsByResultIndex(request) {
|
|
129
|
+
const latestCalls = new Map();
|
|
130
|
+
const resultCalls = new Map();
|
|
131
|
+
for (let index = 0; index < request.items.length; index += 1) {
|
|
132
|
+
const item = request.items[index];
|
|
133
|
+
if (item.type === "tool_call") {
|
|
134
|
+
const name = skillCallName(item);
|
|
135
|
+
latestCalls.set(item.callId, name === undefined ? null : { name, callIndex: index });
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
if (item.type !== "tool_result")
|
|
139
|
+
continue;
|
|
140
|
+
const call = latestCalls.get(item.callId);
|
|
141
|
+
if (call !== undefined && call !== null)
|
|
142
|
+
resultCalls.set(index, call);
|
|
143
|
+
}
|
|
144
|
+
return resultCalls;
|
|
145
|
+
}
|
|
146
|
+
function skillExpansionEvidence(item) {
|
|
147
|
+
if (item.type !== "message" || item.role !== "user" || item.content.length === 0) {
|
|
148
|
+
return undefined;
|
|
149
|
+
}
|
|
150
|
+
const evidence = [];
|
|
151
|
+
for (const part of item.content) {
|
|
152
|
+
if (part.type !== "text")
|
|
153
|
+
return undefined;
|
|
154
|
+
if (isSkillExpansion(part.text)) {
|
|
155
|
+
evidence.push({});
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
const name = alreadyLoadedSkillName(part.text);
|
|
159
|
+
if (name === undefined)
|
|
160
|
+
return undefined;
|
|
161
|
+
evidence.push({ name });
|
|
162
|
+
}
|
|
163
|
+
return evidence;
|
|
164
|
+
}
|
|
165
|
+
function isForkedSkillResult(output, name) {
|
|
166
|
+
const normalized = withoutBudgetReminders(output);
|
|
167
|
+
if (normalized === undefined)
|
|
168
|
+
return false;
|
|
169
|
+
const launched = `Skill "${name}" launched (forked execution, running in the background).`;
|
|
170
|
+
if (normalized === launched
|
|
171
|
+
|| normalized === `${launched}\n\nRunning in the background as @${name}`) {
|
|
172
|
+
return true;
|
|
173
|
+
}
|
|
174
|
+
const completed = `Skill "${name}" completed (forked execution).\n\nResult:\n`;
|
|
175
|
+
return normalized.startsWith(completed)
|
|
176
|
+
&& normalized.slice(completed.length).trim().length > 0;
|
|
177
|
+
}
|
|
178
|
+
function isInlineSkillResult(output, name) {
|
|
179
|
+
return withoutBudgetReminders(output) === `Launching skill: ${name}`;
|
|
180
|
+
}
|
|
181
|
+
function withoutBudgetReminders(output) {
|
|
182
|
+
const firstReminder = output.indexOf(BUDGET_REMINDER_PREFIX);
|
|
183
|
+
if (firstReminder < 0)
|
|
184
|
+
return output;
|
|
185
|
+
let cursor = firstReminder;
|
|
186
|
+
while (cursor < output.length) {
|
|
187
|
+
if (!output.startsWith(BUDGET_REMINDER_PREFIX, cursor))
|
|
188
|
+
return undefined;
|
|
189
|
+
const valueStart = cursor + BUDGET_REMINDER_PREFIX.length;
|
|
190
|
+
const valueEnd = output.indexOf(BUDGET_REMINDER_SUFFIX, valueStart);
|
|
191
|
+
if (valueEnd < 0)
|
|
192
|
+
return undefined;
|
|
193
|
+
const value = output.slice(valueStart, valueEnd);
|
|
194
|
+
if (value.length === 0 || value.length > 256 || /[\n<>]/u.test(value)) {
|
|
195
|
+
return undefined;
|
|
196
|
+
}
|
|
197
|
+
cursor = valueEnd + BUDGET_REMINDER_SUFFIX.length;
|
|
198
|
+
}
|
|
199
|
+
return output.slice(0, firstReminder);
|
|
200
|
+
}
|
|
201
|
+
function latestUserBeforeCallIncludesDirectSkill(request, callIndex, name, availableSkills) {
|
|
202
|
+
for (let index = callIndex - 1; index >= 0; index -= 1) {
|
|
203
|
+
const item = request.items[index];
|
|
204
|
+
if (item.type !== "message" || item.role !== "user")
|
|
205
|
+
continue;
|
|
206
|
+
return directSkillNames(item, availableSkills).includes(name);
|
|
207
|
+
}
|
|
208
|
+
return false;
|
|
209
|
+
}
|
|
210
|
+
function alreadyLoadedSkillName(text) {
|
|
211
|
+
const prefix = "Skill /";
|
|
212
|
+
const marker = " is already loaded above; instructions unchanged.";
|
|
213
|
+
if (!text.startsWith(prefix))
|
|
214
|
+
return undefined;
|
|
215
|
+
const markerIndex = text.indexOf(marker, prefix.length);
|
|
216
|
+
if (markerIndex < 0)
|
|
217
|
+
return undefined;
|
|
218
|
+
const suffix = text.slice(markerIndex + marker.length);
|
|
219
|
+
if (suffix !== "" && !suffix.startsWith(" Arguments:"))
|
|
220
|
+
return undefined;
|
|
221
|
+
return normalizedClaudeSkillName(text.slice(prefix.length, markerIndex));
|
|
222
|
+
}
|
|
223
|
+
function directSkillNames(message, availableSkills) {
|
|
224
|
+
const names = [];
|
|
225
|
+
for (let index = 0; index < message.content.length; index += 1) {
|
|
226
|
+
const command = message.content[index];
|
|
227
|
+
const expansion = message.content[index + 1];
|
|
228
|
+
if (command.type === "text" && expansion?.type === "text") {
|
|
229
|
+
const name = commandName(command.text);
|
|
230
|
+
if (name !== undefined
|
|
231
|
+
&& (isSkillExpansion(expansion.text)
|
|
232
|
+
|| (availableSkills.has(name) && isLegacyCommandBody(expansion.text)))) {
|
|
233
|
+
names.push(name);
|
|
234
|
+
index += 1;
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
const backgroundName = directBackgroundSkillName(message, index);
|
|
239
|
+
if (backgroundName !== undefined)
|
|
240
|
+
names.push(backgroundName);
|
|
241
|
+
}
|
|
242
|
+
return names;
|
|
243
|
+
}
|
|
244
|
+
function isLegacyCommandBody(text) {
|
|
245
|
+
const trimmed = text.trim();
|
|
246
|
+
return trimmed.length > 0
|
|
247
|
+
&& !trimmed.startsWith("<system-reminder>")
|
|
248
|
+
&& !trimmed.startsWith("<local-command-")
|
|
249
|
+
&& !trimmed.startsWith("<command-");
|
|
250
|
+
}
|
|
251
|
+
function directBackgroundSkillName(message, index) {
|
|
252
|
+
const current = message.content[index];
|
|
253
|
+
const previous = message.content[index - 1];
|
|
254
|
+
if (current?.type !== "text" || previous?.type !== "text")
|
|
255
|
+
return undefined;
|
|
256
|
+
const lines = current.text.trimEnd().split("\n");
|
|
257
|
+
if (lines.length !== 2)
|
|
258
|
+
return undefined;
|
|
259
|
+
const stdoutPrefix = "<local-command-stdout>Running in the background as @";
|
|
260
|
+
const stdoutSuffix = "</local-command-stdout>";
|
|
261
|
+
if (!lines[0]?.startsWith(stdoutPrefix) || !lines[0].endsWith(stdoutSuffix)) {
|
|
262
|
+
return undefined;
|
|
263
|
+
}
|
|
264
|
+
const stdoutName = normalizedClaudeSkillName(lines[0].slice(stdoutPrefix.length, -stdoutSuffix.length));
|
|
265
|
+
const launchPrefix = "<forked-skill-launch>";
|
|
266
|
+
const launchSuffix = "</forked-skill-launch>";
|
|
267
|
+
const launchLine = lines[1];
|
|
268
|
+
if (!launchLine?.startsWith(launchPrefix) || !launchLine.endsWith(launchSuffix)) {
|
|
269
|
+
return undefined;
|
|
270
|
+
}
|
|
271
|
+
let launch;
|
|
272
|
+
try {
|
|
273
|
+
launch = JSON.parse(launchLine.slice(launchPrefix.length, -launchSuffix.length));
|
|
274
|
+
}
|
|
275
|
+
catch {
|
|
276
|
+
return undefined;
|
|
277
|
+
}
|
|
278
|
+
if (!isRecord(launch) || Object.keys(launch).some((key) => key !== "agentId" && key !== "skillName" && key !== "description")) {
|
|
279
|
+
return undefined;
|
|
280
|
+
}
|
|
281
|
+
const name = normalizedClaudeSkillName(launch.skillName);
|
|
282
|
+
if (name === undefined
|
|
283
|
+
|| stdoutName !== name
|
|
284
|
+
|| typeof launch.agentId !== "string"
|
|
285
|
+
|| !/^[a-f0-9]{16,64}$/u.test(launch.agentId)
|
|
286
|
+
|| typeof launch.description !== "string"
|
|
287
|
+
|| (launch.description !== `/${name}` && !launch.description.startsWith(`/${name} `))) {
|
|
288
|
+
return undefined;
|
|
289
|
+
}
|
|
290
|
+
const command = previous.text.trimEnd();
|
|
291
|
+
return command === `/${name}` || command.startsWith(`/${name} `)
|
|
292
|
+
? name
|
|
293
|
+
: undefined;
|
|
294
|
+
}
|
|
295
|
+
function toolResultText(result) {
|
|
296
|
+
if (typeof result.output === "string")
|
|
297
|
+
return result.output;
|
|
298
|
+
if (Array.isArray(result.output)
|
|
299
|
+
&& result.output.length === 1
|
|
300
|
+
&& result.output[0].type === "text") {
|
|
301
|
+
return result.output[0].text;
|
|
302
|
+
}
|
|
303
|
+
return undefined;
|
|
304
|
+
}
|
|
305
|
+
function latestUserMessage(request) {
|
|
306
|
+
for (let index = request.items.length - 1; index >= 0; index -= 1) {
|
|
307
|
+
const item = request.items[index];
|
|
308
|
+
if (item.type === "message" && (item.role === "system" || item.role === "developer")) {
|
|
309
|
+
continue;
|
|
310
|
+
}
|
|
311
|
+
return item.type === "message" && item.role === "user" ? item : undefined;
|
|
312
|
+
}
|
|
313
|
+
return undefined;
|
|
314
|
+
}
|
|
315
|
+
function commandName(text) {
|
|
316
|
+
const firstBreak = text.indexOf("\n");
|
|
317
|
+
const secondBreak = firstBreak < 0 ? -1 : text.indexOf("\n", firstBreak + 1);
|
|
318
|
+
if (firstBreak < 0 || secondBreak < 0)
|
|
319
|
+
return undefined;
|
|
320
|
+
const firstLine = text.slice(0, firstBreak);
|
|
321
|
+
const secondLine = text.slice(firstBreak + 1, secondBreak);
|
|
322
|
+
const rest = text.slice(secondBreak + 1);
|
|
323
|
+
const name = exactTagValue(firstLine, "command-message");
|
|
324
|
+
const hasExactArguments = rest.startsWith("<command-args>")
|
|
325
|
+
&& (rest.endsWith("</command-args>") || rest.endsWith("</command-args>\n"));
|
|
326
|
+
if (name === undefined
|
|
327
|
+
|| secondLine !== `<command-name>/${name}</command-name>`
|
|
328
|
+
|| (rest !== "" && !hasExactArguments)) {
|
|
329
|
+
return undefined;
|
|
330
|
+
}
|
|
331
|
+
return normalizedClaudeSkillName(name);
|
|
332
|
+
}
|
|
333
|
+
function isSkillExpansion(text) {
|
|
334
|
+
const prefix = "Base directory for this skill: ";
|
|
335
|
+
if (!text.startsWith(prefix))
|
|
336
|
+
return false;
|
|
337
|
+
const pathEnd = text.indexOf("\n", prefix.length);
|
|
338
|
+
if (pathEnd < 0 || !text.slice(pathEnd).startsWith("\n\n"))
|
|
339
|
+
return false;
|
|
340
|
+
const path = normalizedSkillPath(text.slice(prefix.length, pathEnd));
|
|
341
|
+
return path !== undefined && text.slice(pathEnd + 2).trim().length > 0;
|
|
342
|
+
}
|
|
343
|
+
function exactTagValue(line, tag) {
|
|
344
|
+
const prefix = `<${tag}>`;
|
|
345
|
+
const suffix = `</${tag}>`;
|
|
346
|
+
return line.startsWith(prefix) && line.endsWith(suffix)
|
|
347
|
+
? line.slice(prefix.length, -suffix.length)
|
|
348
|
+
: undefined;
|
|
349
|
+
}
|
|
350
|
+
function observation(name, toolCallId, evidenceSource) {
|
|
351
|
+
return {
|
|
352
|
+
eventType: "skill_use",
|
|
353
|
+
toolCallId,
|
|
354
|
+
capabilityId: claudeSkillCapabilityId(name),
|
|
355
|
+
observedName: name,
|
|
356
|
+
evidenceSource,
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
function normalizedSkillPath(value) {
|
|
360
|
+
const normalized = value.normalize("NFC");
|
|
361
|
+
return normalized.length > 0
|
|
362
|
+
&& normalized.length <= MAX_SKILL_PATH_CHARS
|
|
363
|
+
&& !UNSAFE_DISPLAY_CHARACTERS.test(normalized)
|
|
364
|
+
? normalized
|
|
365
|
+
: undefined;
|
|
366
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { InteractionRequest } from "../../../interaction/types.js";
|
|
2
|
+
import { type NormalizedSkillPath, type PathPlatform } from "../paths.js";
|
|
3
|
+
export type CatalogSkill = {
|
|
4
|
+
name: string;
|
|
5
|
+
path: NormalizedSkillPath;
|
|
6
|
+
};
|
|
7
|
+
export type CodexSkillCatalog = {
|
|
8
|
+
platform: PathPlatform;
|
|
9
|
+
byPath: ReadonlyMap<string, CatalogSkill>;
|
|
10
|
+
};
|
|
11
|
+
export declare function codexFileSkillCatalog(request: InteractionRequest): CodexSkillCatalog | undefined;
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { normalizedCodexSkillName } from "./identity.js";
|
|
2
|
+
import { joinedSkillPath, normalizedAbsoluteSkillPath, skillPathBasename, } from "../paths.js";
|
|
3
|
+
const MAX_CATALOG_ENTRIES = 2_000;
|
|
4
|
+
const SKILLS_OPEN = "<skills_instructions>";
|
|
5
|
+
const SKILLS_CLOSE = "</skills_instructions>";
|
|
6
|
+
const SKILL_ROOTS = "### Skill roots\n";
|
|
7
|
+
const SKILLS_LIST = "### Available skills\n";
|
|
8
|
+
const FILE_LOCATOR = " (file: ";
|
|
9
|
+
const SKILL_ROOT = /^- `(?<alias>r[0-9]+)` = `(?<path>[^`\n]+)`$/u;
|
|
10
|
+
export function codexFileSkillCatalog(request) {
|
|
11
|
+
const instructionTexts = request.items.flatMap((item) => {
|
|
12
|
+
if (item.type !== "message"
|
|
13
|
+
|| (item.role !== "developer" && item.role !== "system"))
|
|
14
|
+
return [];
|
|
15
|
+
return item.content.flatMap((part) => part.type === "text" ? [part.text] : []);
|
|
16
|
+
});
|
|
17
|
+
if (request.instructions !== undefined)
|
|
18
|
+
instructionTexts.push(request.instructions);
|
|
19
|
+
let latestCatalog;
|
|
20
|
+
for (const instructionText of instructionTexts) {
|
|
21
|
+
const blocks = skillInstructionBlocks(normalizedLineEndings(instructionText));
|
|
22
|
+
if (blocks === undefined) {
|
|
23
|
+
latestCatalog = undefined;
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
for (const block of blocks) {
|
|
27
|
+
const listStart = block.indexOf(SKILLS_LIST);
|
|
28
|
+
const roots = listStart < 0
|
|
29
|
+
? undefined
|
|
30
|
+
: catalogRoots(block.slice(0, listStart));
|
|
31
|
+
latestCatalog = listStart < 0 || roots === undefined
|
|
32
|
+
? undefined
|
|
33
|
+
: catalogEntries(block.slice(listStart + SKILLS_LIST.length).split("\n"), roots);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return latestCatalog;
|
|
37
|
+
}
|
|
38
|
+
function catalogRoots(blockPrefix) {
|
|
39
|
+
const roots = new Map();
|
|
40
|
+
const rootsStart = blockPrefix.indexOf(SKILL_ROOTS);
|
|
41
|
+
if (rootsStart < 0)
|
|
42
|
+
return roots;
|
|
43
|
+
let platform;
|
|
44
|
+
for (const line of blockPrefix.slice(rootsStart + SKILL_ROOTS.length).split("\n")) {
|
|
45
|
+
if (line.startsWith("### "))
|
|
46
|
+
break;
|
|
47
|
+
const match = line.match(SKILL_ROOT);
|
|
48
|
+
if (match?.groups === undefined)
|
|
49
|
+
continue;
|
|
50
|
+
const path = normalizedAbsoluteSkillPath(match.groups.path);
|
|
51
|
+
if (path === undefined
|
|
52
|
+
|| roots.has(match.groups.alias)
|
|
53
|
+
|| (platform !== undefined && path.platform !== platform))
|
|
54
|
+
return undefined;
|
|
55
|
+
platform = path.platform;
|
|
56
|
+
roots.set(match.groups.alias, path);
|
|
57
|
+
if (roots.size > MAX_CATALOG_ENTRIES)
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
return roots;
|
|
61
|
+
}
|
|
62
|
+
function catalogEntries(lines, roots) {
|
|
63
|
+
const catalog = new Map();
|
|
64
|
+
const ambiguousPaths = new Set();
|
|
65
|
+
let entryCount = 0;
|
|
66
|
+
let platform;
|
|
67
|
+
for (const line of lines) {
|
|
68
|
+
if (line.startsWith("### "))
|
|
69
|
+
break;
|
|
70
|
+
const skill = catalogSkill(line, roots);
|
|
71
|
+
if (skill === undefined)
|
|
72
|
+
continue;
|
|
73
|
+
entryCount += 1;
|
|
74
|
+
if (entryCount > MAX_CATALOG_ENTRIES
|
|
75
|
+
|| (platform !== undefined && skill.path.platform !== platform))
|
|
76
|
+
return undefined;
|
|
77
|
+
platform = skill.path.platform;
|
|
78
|
+
if (ambiguousPaths.has(skill.path.key))
|
|
79
|
+
continue;
|
|
80
|
+
const previous = catalog.get(skill.path.key);
|
|
81
|
+
if (previous !== undefined && previous.name !== skill.name) {
|
|
82
|
+
catalog.delete(skill.path.key);
|
|
83
|
+
ambiguousPaths.add(skill.path.key);
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
catalog.set(skill.path.key, skill);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return platform === undefined || catalog.size === 0
|
|
90
|
+
? undefined
|
|
91
|
+
: { platform, byPath: catalog };
|
|
92
|
+
}
|
|
93
|
+
function catalogSkill(line, roots) {
|
|
94
|
+
if (!line.startsWith("- ") || !line.endsWith(")"))
|
|
95
|
+
return undefined;
|
|
96
|
+
const nameEnd = line.indexOf(": ", 2);
|
|
97
|
+
const locatorStart = line.lastIndexOf(FILE_LOCATOR);
|
|
98
|
+
if (nameEnd < 0 || locatorStart <= nameEnd)
|
|
99
|
+
return undefined;
|
|
100
|
+
const name = normalizedCodexSkillName(line.slice(2, nameEnd));
|
|
101
|
+
const path = catalogPath(line.slice(locatorStart + FILE_LOCATOR.length, -1), roots);
|
|
102
|
+
return name === undefined
|
|
103
|
+
|| path === undefined
|
|
104
|
+
|| skillPathBasename(path).toLowerCase() !== "skill.md"
|
|
105
|
+
? undefined
|
|
106
|
+
: { name, path };
|
|
107
|
+
}
|
|
108
|
+
function catalogPath(locator, roots) {
|
|
109
|
+
const absolute = normalizedAbsoluteSkillPath(locator);
|
|
110
|
+
if (absolute !== undefined)
|
|
111
|
+
return absolute;
|
|
112
|
+
const separator = locator.indexOf("/");
|
|
113
|
+
if (separator <= 0)
|
|
114
|
+
return undefined;
|
|
115
|
+
const root = roots.get(locator.slice(0, separator));
|
|
116
|
+
return root === undefined
|
|
117
|
+
? undefined
|
|
118
|
+
: joinedSkillPath(root, locator.slice(separator + 1));
|
|
119
|
+
}
|
|
120
|
+
function normalizedLineEndings(value) {
|
|
121
|
+
return value.replaceAll("\r\n", "\n").replaceAll("\r", "\n");
|
|
122
|
+
}
|
|
123
|
+
function skillInstructionBlocks(text) {
|
|
124
|
+
const blocks = [];
|
|
125
|
+
let cursor = 0;
|
|
126
|
+
while (cursor < text.length) {
|
|
127
|
+
const start = text.indexOf(SKILLS_OPEN, cursor);
|
|
128
|
+
if (start < 0)
|
|
129
|
+
break;
|
|
130
|
+
const bodyStart = start + SKILLS_OPEN.length;
|
|
131
|
+
const end = text.indexOf(SKILLS_CLOSE, bodyStart);
|
|
132
|
+
if (end < 0)
|
|
133
|
+
return undefined;
|
|
134
|
+
blocks.push(text.slice(bodyStart, end));
|
|
135
|
+
cursor = end + SKILLS_CLOSE.length;
|
|
136
|
+
}
|
|
137
|
+
return blocks;
|
|
138
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { InteractionOutput } from "../../../interaction/types.js";
|
|
2
|
+
import type { CatalogSkill, CodexSkillCatalog } from "./catalog.js";
|
|
3
|
+
export type CodexExecCommand = {
|
|
4
|
+
callId: string;
|
|
5
|
+
command: string;
|
|
6
|
+
workdir?: string;
|
|
7
|
+
};
|
|
8
|
+
export declare function codexExecCommand(item: InteractionOutput["items"][number]): CodexExecCommand | undefined;
|
|
9
|
+
export declare function readSkillsFromCodexCommand(input: CodexExecCommand, catalog: CodexSkillCatalog): CatalogSkill[];
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { interactionToolInput } from "../../tools/input.js";
|
|
2
|
+
import { readPathsFromShellCommand } from "../shell_file_reads.js";
|
|
3
|
+
const MAX_COMMAND_CHARS = 64 * 1024;
|
|
4
|
+
const MAX_SKILL_PATH_CHARS = 16 * 1024;
|
|
5
|
+
const UNSAFE_DISPLAY_CHARACTERS = /[\u0000-\u001f\u007f-\u009f\u202a-\u202e\u2066-\u2069]/u;
|
|
6
|
+
const UNSAFE_COMMAND_CHARACTERS = /[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f-\u009f\u202a-\u202e\u2066-\u2069]/u;
|
|
7
|
+
export function codexExecCommand(item) {
|
|
8
|
+
if (item.type !== "tool_call"
|
|
9
|
+
|| item.kind !== "function"
|
|
10
|
+
|| item.namespace !== undefined
|
|
11
|
+
|| item.name !== "exec_command")
|
|
12
|
+
return undefined;
|
|
13
|
+
const input = interactionToolInput(item);
|
|
14
|
+
if (input === undefined)
|
|
15
|
+
return undefined;
|
|
16
|
+
const command = input.cmd;
|
|
17
|
+
const workdir = input.workdir;
|
|
18
|
+
if (typeof command !== "string"
|
|
19
|
+
|| command.length === 0
|
|
20
|
+
|| command.length > MAX_COMMAND_CHARS
|
|
21
|
+
|| UNSAFE_COMMAND_CHARACTERS.test(command)
|
|
22
|
+
|| (workdir !== undefined && (typeof workdir !== "string"
|
|
23
|
+
|| workdir.length === 0
|
|
24
|
+
|| workdir.length > MAX_SKILL_PATH_CHARS
|
|
25
|
+
|| UNSAFE_DISPLAY_CHARACTERS.test(workdir))))
|
|
26
|
+
return undefined;
|
|
27
|
+
return workdir === undefined
|
|
28
|
+
? { callId: item.callId, command }
|
|
29
|
+
: { callId: item.callId, command, workdir };
|
|
30
|
+
}
|
|
31
|
+
export function readSkillsFromCodexCommand(input, catalog) {
|
|
32
|
+
return readPathsFromShellCommand(input, catalog.platform).flatMap((path) => {
|
|
33
|
+
const skill = catalog.byPath.get(path.key);
|
|
34
|
+
return skill === undefined ? [] : [skill];
|
|
35
|
+
});
|
|
36
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { InteractionRequest } from "../../../interaction/types.js";
|
|
2
|
+
import type { SkillObservation } from "../observations.js";
|
|
3
|
+
export declare function codexExplicitSkillObservations(request: InteractionRequest, currentTurnId: string, limit: number): SkillObservation[];
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { codexSkillCapabilityId, normalizedCodexSkillName, } from "./identity.js";
|
|
2
|
+
const MAX_SKILL_PATH_CHARS = 16 * 1024;
|
|
3
|
+
const UNSAFE_DISPLAY_CHARACTERS = /[\u0000-\u001f\u007f-\u009f\u202a-\u202e\u2066-\u2069]/u;
|
|
4
|
+
const SKILL_MENTION = /\$([A-Za-z0-9][A-Za-z0-9_:-]*)/gu;
|
|
5
|
+
export function codexExplicitSkillObservations(request, currentTurnId, limit) {
|
|
6
|
+
let currentUserStart = request.items.length;
|
|
7
|
+
while (currentUserStart > 0) {
|
|
8
|
+
const item = request.items[currentUserStart - 1];
|
|
9
|
+
if (item.type !== "message" || item.role !== "user")
|
|
10
|
+
break;
|
|
11
|
+
currentUserStart -= 1;
|
|
12
|
+
}
|
|
13
|
+
const parsed = [];
|
|
14
|
+
for (let index = currentUserStart; index < request.items.length; index += 1) {
|
|
15
|
+
const message = request.items[index];
|
|
16
|
+
if (message.type !== "message" || message.role !== "user")
|
|
17
|
+
continue;
|
|
18
|
+
parsed.push({ message, skill: parseCodexSkillMessage(message) });
|
|
19
|
+
}
|
|
20
|
+
const mentionedNames = new Set();
|
|
21
|
+
const observed = [];
|
|
22
|
+
const observedInjections = new Set();
|
|
23
|
+
for (const { message, skill } of parsed) {
|
|
24
|
+
if (skill !== undefined) {
|
|
25
|
+
const injection = skillInjectionKey(skill);
|
|
26
|
+
if ((message.clientTurnId?.toLowerCase() === currentTurnId.toLowerCase()
|
|
27
|
+
|| (message.clientTurnId === undefined
|
|
28
|
+
&& mentionedNames.has(skill.name)))
|
|
29
|
+
&& !observedInjections.has(injection)) {
|
|
30
|
+
observedInjections.add(injection);
|
|
31
|
+
observed.push(observation(skill.name));
|
|
32
|
+
if (observed.length === limit)
|
|
33
|
+
break;
|
|
34
|
+
}
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
for (const match of textParts(message).join("").matchAll(SKILL_MENTION)) {
|
|
38
|
+
if (match[1] !== undefined)
|
|
39
|
+
mentionedNames.add(match[1]);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return observed;
|
|
43
|
+
}
|
|
44
|
+
function observation(name) {
|
|
45
|
+
return {
|
|
46
|
+
eventType: "skill_use",
|
|
47
|
+
toolCallId: null,
|
|
48
|
+
capabilityId: codexSkillCapabilityId(name),
|
|
49
|
+
observedName: name,
|
|
50
|
+
evidenceSource: "skill_content_injection",
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
function parseCodexSkillMessage(message) {
|
|
54
|
+
if (message.content.length !== 1 || message.content[0].type !== "text") {
|
|
55
|
+
return undefined;
|
|
56
|
+
}
|
|
57
|
+
const text = message.content[0].text;
|
|
58
|
+
const prefix = "<skill>\n<name>";
|
|
59
|
+
if (!text.startsWith(prefix) || !text.endsWith("\n</skill>"))
|
|
60
|
+
return undefined;
|
|
61
|
+
const nameEnd = text.indexOf("</name>\n<path>", prefix.length);
|
|
62
|
+
if (nameEnd < 0)
|
|
63
|
+
return undefined;
|
|
64
|
+
const pathStart = nameEnd + "</name>\n<path>".length;
|
|
65
|
+
const pathEnd = text.indexOf("</path>\n", pathStart);
|
|
66
|
+
if (pathEnd < 0)
|
|
67
|
+
return undefined;
|
|
68
|
+
const bodyStart = pathEnd + "</path>\n".length;
|
|
69
|
+
const bodyEnd = text.length - "\n</skill>".length;
|
|
70
|
+
if (bodyEnd <= bodyStart || text.slice(bodyStart, bodyEnd).trim().length === 0) {
|
|
71
|
+
return undefined;
|
|
72
|
+
}
|
|
73
|
+
const name = normalizedCodexSkillName(text.slice(prefix.length, nameEnd));
|
|
74
|
+
const path = normalizedSkillPath(text.slice(pathStart, pathEnd));
|
|
75
|
+
return name === undefined || path === undefined ? undefined : { name, path };
|
|
76
|
+
}
|
|
77
|
+
function skillInjectionKey(skill) {
|
|
78
|
+
return `${skill.name}\u0000${skill.path}`;
|
|
79
|
+
}
|
|
80
|
+
function normalizedSkillPath(value) {
|
|
81
|
+
const normalized = value.normalize("NFC");
|
|
82
|
+
return normalized.length > 0
|
|
83
|
+
&& normalized.length <= MAX_SKILL_PATH_CHARS
|
|
84
|
+
&& !UNSAFE_DISPLAY_CHARACTERS.test(normalized)
|
|
85
|
+
? normalized
|
|
86
|
+
: undefined;
|
|
87
|
+
}
|
|
88
|
+
function textParts(message) {
|
|
89
|
+
return message.content.flatMap((part) => part.type === "text" ? [part.text] : []);
|
|
90
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { InteractionOutput, InteractionRequest } from "../../../interaction/types.js";
|
|
2
|
+
import type { SkillObservation } from "../observations.js";
|
|
3
|
+
/** Match model-emitted Codex file reads to the exact current skill catalog. */
|
|
4
|
+
export declare function codexAutomaticSkillObservations(request: InteractionRequest, output: InteractionOutput, limit: number): SkillObservation[];
|