@mupt-ai/dari-gateway 0.1.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/README.md +8 -1
- 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 +22 -5
- package/src/bun.ts +4 -0
- 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 +17 -17
- 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 +10 -9
- 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 +10 -10
- 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/interaction/types.ts +3 -0
- 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 +36 -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 +115 -41
- package/src/providers/pi_context.ts +6 -6
- package/src/providers/pi_options.ts +283 -453
- package/src/providers/pi_output.ts +6 -5
- package/src/providers/provider_attempt_runner.ts +1 -1
- package/src/providers/provider_call.ts +37 -18
- package/src/providers/provider_reported_cost.ts +138 -0
- package/src/providers/reasoning.ts +2 -2
- package/src/providers/stream_finalization.ts +10 -10
- 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 +31 -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 +46 -10
- package/src/runtime/provider_stream_lifecycle.ts +1 -1
- package/src/runtime/provider_stream_outcome.ts +2 -2
- package/src/runtime/request_tags.ts +34 -0
- package/src/runtime/routed_completion.ts +13 -11
- package/src/runtime/routed_protocol.ts +2 -2
- package/src/runtime/routed_runtime.ts +38 -20
- package/src/runtime/routing_lease.ts +7 -7
- package/src/runtime/runtime_reporter.ts +3 -3
- package/src/self_host/main.ts +19 -14
- package/src/self_host/platform.ts +5 -5
- package/src/self_host/prefix_store.ts +2 -2
- package/src/self_host/resolve.ts +11 -11
- 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 +11 -11
- 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 +30 -25
- 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 +9 -5
- package/src/types.ts +15 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { codexExecCommand, readSkillsFromCodexCommand, } from "./exec_file_reads.js";
|
|
2
|
+
import { codexFileSkillCatalog } from "./catalog.js";
|
|
3
|
+
import { codexSkillCapabilityId } from "./identity.js";
|
|
4
|
+
/** Match model-emitted Codex file reads to the exact current skill catalog. */
|
|
5
|
+
export function codexAutomaticSkillObservations(request, output, limit) {
|
|
6
|
+
if (limit <= 0
|
|
7
|
+
|| request.sourceProtocol !== "openai_responses"
|
|
8
|
+
|| !request.tools.some((tool) => tool.kind === "function"
|
|
9
|
+
&& tool.name === "exec_command"
|
|
10
|
+
&& tool.namespace === undefined))
|
|
11
|
+
return [];
|
|
12
|
+
const catalog = codexFileSkillCatalog(request);
|
|
13
|
+
if (catalog === undefined)
|
|
14
|
+
return [];
|
|
15
|
+
const observedPaths = new Set();
|
|
16
|
+
const observed = [];
|
|
17
|
+
for (const item of output.items) {
|
|
18
|
+
const input = codexExecCommand(item);
|
|
19
|
+
if (input === undefined)
|
|
20
|
+
continue;
|
|
21
|
+
for (const skill of readSkillsFromCodexCommand(input, catalog)) {
|
|
22
|
+
if (observedPaths.has(skill.path.key))
|
|
23
|
+
continue;
|
|
24
|
+
observedPaths.add(skill.path.key);
|
|
25
|
+
observed.push({
|
|
26
|
+
eventType: "skill_load",
|
|
27
|
+
toolCallId: input.callId,
|
|
28
|
+
capabilityId: codexSkillCapabilityId(skill.name),
|
|
29
|
+
observedName: skill.name,
|
|
30
|
+
evidenceSource: "skill_file_read",
|
|
31
|
+
});
|
|
32
|
+
if (observed.length === limit)
|
|
33
|
+
return observed;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return observed;
|
|
37
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
const CAPABILITY_PREFIX = "codex:name:";
|
|
2
|
+
const MAX_CAPABILITY_ID_CHARS = 512;
|
|
3
|
+
const MAX_SKILL_NAME_CHARS = 512;
|
|
4
|
+
const SKILL_NAME = /^[A-Za-z0-9][A-Za-z0-9_:-]*$/u;
|
|
5
|
+
const UNSAFE_DISPLAY_CHARACTERS = /[\u0000-\u001f\u007f-\u009f\u202a-\u202e\u2066-\u2069]/u;
|
|
6
|
+
export function codexSkillCapabilityId(name) {
|
|
7
|
+
return `${CAPABILITY_PREFIX}${name}`;
|
|
8
|
+
}
|
|
9
|
+
export function normalizedCodexSkillName(value) {
|
|
10
|
+
if (typeof value !== "string")
|
|
11
|
+
return undefined;
|
|
12
|
+
const normalized = value.normalize("NFC").trim();
|
|
13
|
+
return normalized.length > 0
|
|
14
|
+
&& normalized.length <= MAX_SKILL_NAME_CHARS
|
|
15
|
+
&& normalized.length <= MAX_CAPABILITY_ID_CHARS - CAPABILITY_PREFIX.length
|
|
16
|
+
&& SKILL_NAME.test(normalized)
|
|
17
|
+
&& !UNSAFE_DISPLAY_CHARACTERS.test(normalized)
|
|
18
|
+
? normalized
|
|
19
|
+
: undefined;
|
|
20
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type InteractionOutput, type InteractionRequest } from "../../interaction/types.js";
|
|
2
|
+
import type { HarnessConversationIdentity } from "../../runtime/harness_identity/index.js";
|
|
3
|
+
export type SkillEvidenceSource = "skill_content_injection" | "skill_command_expansion" | "skill_tool_activation" | "skill_file_read";
|
|
4
|
+
export type SkillObservation = {
|
|
5
|
+
eventType: "skill_use" | "skill_load";
|
|
6
|
+
toolCallId: string | null;
|
|
7
|
+
capabilityId: string;
|
|
8
|
+
observedName: string;
|
|
9
|
+
evidenceSource: SkillEvidenceSource;
|
|
10
|
+
};
|
|
11
|
+
export declare function inputSkillObservations(args: {
|
|
12
|
+
request: InteractionRequest;
|
|
13
|
+
harnessConversationIdentity?: HarnessConversationIdentity;
|
|
14
|
+
limit: number;
|
|
15
|
+
}): SkillObservation[];
|
|
16
|
+
export declare function outputSkillObservations(args: {
|
|
17
|
+
request: InteractionRequest;
|
|
18
|
+
output: InteractionOutput;
|
|
19
|
+
harnessConversationIdentity?: HarnessConversationIdentity;
|
|
20
|
+
limit: number;
|
|
21
|
+
}): SkillObservation[];
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { isBackgroundRequest } from "../../interaction/types.js";
|
|
2
|
+
import { piStyleAutomaticSkillObservations, piStyleExplicitSkillObservations, } from "./pi.js";
|
|
3
|
+
import { claudeFileReadObservations } from "./claude/file_reads.js";
|
|
4
|
+
import { claudeSkillObservations } from "./claude/observations.js";
|
|
5
|
+
import { codexExplicitSkillObservations } from "./codex/explicit.js";
|
|
6
|
+
import { codexAutomaticSkillObservations } from "./codex/file_reads.js";
|
|
7
|
+
export function inputSkillObservations(args) {
|
|
8
|
+
if (args.limit <= 0 || isBackgroundRequest(args.request))
|
|
9
|
+
return [];
|
|
10
|
+
if (args.request.requestPurpose === undefined
|
|
11
|
+
&& args.request.sourceProtocol === "openai_chat") {
|
|
12
|
+
return piStyleExplicitSkillObservations(args.request, args.limit);
|
|
13
|
+
}
|
|
14
|
+
if (args.request.requestPurpose === undefined
|
|
15
|
+
&& args.request.sourceProtocol === "openai_responses"
|
|
16
|
+
&& args.harnessConversationIdentity?.kind === "codex_thread"
|
|
17
|
+
&& args.harnessConversationIdentity.userTurnId !== undefined) {
|
|
18
|
+
return codexExplicitSkillObservations(args.request, args.harnessConversationIdentity.userTurnId, args.limit);
|
|
19
|
+
}
|
|
20
|
+
if (args.request.sourceProtocol === "anthropic_messages"
|
|
21
|
+
&& args.harnessConversationIdentity?.kind === "claude_code_session") {
|
|
22
|
+
return claudeSkillObservations(args.request, args.limit);
|
|
23
|
+
}
|
|
24
|
+
return [];
|
|
25
|
+
}
|
|
26
|
+
export function outputSkillObservations(args) {
|
|
27
|
+
if (args.limit <= 0 || isBackgroundRequest(args.request))
|
|
28
|
+
return [];
|
|
29
|
+
if (args.request.sourceProtocol === "openai_chat") {
|
|
30
|
+
return piStyleAutomaticSkillObservations(args.request, args.output, args.limit);
|
|
31
|
+
}
|
|
32
|
+
if (args.request.sourceProtocol === "openai_responses"
|
|
33
|
+
&& args.harnessConversationIdentity?.kind === "codex_thread") {
|
|
34
|
+
return codexAutomaticSkillObservations(args.request, args.output, args.limit);
|
|
35
|
+
}
|
|
36
|
+
if (args.request.sourceProtocol === "anthropic_messages"
|
|
37
|
+
&& args.harnessConversationIdentity?.kind === "claude_code_session") {
|
|
38
|
+
return claudeFileReadObservations(args.request, args.output, args.limit);
|
|
39
|
+
}
|
|
40
|
+
return [];
|
|
41
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type PathPlatform = "posix" | "windows";
|
|
2
|
+
export type NormalizedSkillPath = {
|
|
3
|
+
platform: PathPlatform;
|
|
4
|
+
value: string;
|
|
5
|
+
key: string;
|
|
6
|
+
};
|
|
7
|
+
export declare function normalizedAbsoluteSkillPath(value: unknown): NormalizedSkillPath | undefined;
|
|
8
|
+
export declare function resolvedSkillPath(value: string, workdir: NormalizedSkillPath | undefined): NormalizedSkillPath | undefined;
|
|
9
|
+
export declare function joinedSkillPath(root: NormalizedSkillPath, suffix: string): NormalizedSkillPath | undefined;
|
|
10
|
+
export declare function skillPathBasename(path: NormalizedSkillPath): string;
|
|
11
|
+
export declare function skillPathSegments(path: NormalizedSkillPath): string[];
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { posix, win32 } from "node:path";
|
|
2
|
+
const MAX_SKILL_PATH_CHARS = 16 * 1024;
|
|
3
|
+
const UNSAFE_DISPLAY_CHARACTERS = /[\u0000-\u001f\u007f-\u009f\u202a-\u202e\u2066-\u2069]/u;
|
|
4
|
+
export function normalizedAbsoluteSkillPath(value) {
|
|
5
|
+
if (typeof value !== "string"
|
|
6
|
+
|| value.length === 0
|
|
7
|
+
|| value.length > MAX_SKILL_PATH_CHARS
|
|
8
|
+
|| UNSAFE_DISPLAY_CHARACTERS.test(value)
|
|
9
|
+
|| hasParentPathSegment(value))
|
|
10
|
+
return undefined;
|
|
11
|
+
if (posix.isAbsolute(value)) {
|
|
12
|
+
const normalized = posix.normalize(value);
|
|
13
|
+
return { platform: "posix", value: normalized, key: `posix:${normalized}` };
|
|
14
|
+
}
|
|
15
|
+
if (win32.isAbsolute(value)) {
|
|
16
|
+
const normalized = win32.normalize(value);
|
|
17
|
+
return {
|
|
18
|
+
platform: "windows",
|
|
19
|
+
value: normalized,
|
|
20
|
+
key: `windows:${normalized.toLowerCase()}`,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
return undefined;
|
|
24
|
+
}
|
|
25
|
+
export function resolvedSkillPath(value, workdir) {
|
|
26
|
+
if (hasParentPathSegment(value) || /^[A-Za-z]:[^\\/]/u.test(value)) {
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
const absolute = normalizedAbsoluteSkillPath(value);
|
|
30
|
+
if (absolute !== undefined)
|
|
31
|
+
return absolute;
|
|
32
|
+
if (workdir === undefined)
|
|
33
|
+
return undefined;
|
|
34
|
+
const resolved = workdir.platform === "windows"
|
|
35
|
+
? win32.resolve(workdir.value, value)
|
|
36
|
+
: posix.resolve(workdir.value, value);
|
|
37
|
+
const normalized = normalizedAbsoluteSkillPath(resolved);
|
|
38
|
+
return normalized?.platform === workdir.platform ? normalized : undefined;
|
|
39
|
+
}
|
|
40
|
+
export function joinedSkillPath(root, suffix) {
|
|
41
|
+
if (hasParentPathSegment(suffix))
|
|
42
|
+
return undefined;
|
|
43
|
+
const joined = root.platform === "windows"
|
|
44
|
+
? win32.join(root.value, ...suffix.split("/"))
|
|
45
|
+
: posix.join(root.value, suffix);
|
|
46
|
+
const normalized = normalizedAbsoluteSkillPath(joined);
|
|
47
|
+
return normalized?.platform === root.platform ? normalized : undefined;
|
|
48
|
+
}
|
|
49
|
+
export function skillPathBasename(path) {
|
|
50
|
+
return path.platform === "windows"
|
|
51
|
+
? win32.basename(path.value)
|
|
52
|
+
: posix.basename(path.value);
|
|
53
|
+
}
|
|
54
|
+
export function skillPathSegments(path) {
|
|
55
|
+
const implementation = path.platform === "windows" ? win32 : posix;
|
|
56
|
+
const root = implementation.parse(path.value).root;
|
|
57
|
+
return path.value.slice(root.length).split(implementation.sep).filter(Boolean);
|
|
58
|
+
}
|
|
59
|
+
function hasParentPathSegment(value) {
|
|
60
|
+
return value.split(/[\\/]/u).includes("..");
|
|
61
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { InteractionOutput, InteractionRequest } from "../../interaction/types.js";
|
|
2
|
+
import type { SkillObservation } from "./observations.js";
|
|
3
|
+
/** Parse the exact expansion emitted by Pi for `/skill:name [arguments]`. */
|
|
4
|
+
export declare function piStyleExplicitSkillObservations(request: InteractionRequest, limit: number): SkillObservation[];
|
|
5
|
+
/** Match a current model-emitted Pi `read` call to its exact skill catalog entry. */
|
|
6
|
+
export declare function piStyleAutomaticSkillObservations(request: InteractionRequest, output: InteractionOutput, limit: number): SkillObservation[];
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import { interactionToolInput } from "../tools/input.js";
|
|
2
|
+
const CAPABILITY_PREFIX = "unattributed:name:";
|
|
3
|
+
const MAX_CAPABILITY_ID_CHARS = 512;
|
|
4
|
+
const MAX_SKILL_NAME_CHARS = 64;
|
|
5
|
+
const MAX_SKILL_PATH_CHARS = 16 * 1024;
|
|
6
|
+
const PI_SKILL_CLOSE = "\n</skill>";
|
|
7
|
+
const SKILL_NAME = /^[A-Za-z0-9][A-Za-z0-9._-]*$/u;
|
|
8
|
+
const UNSAFE_DISPLAY_CHARACTERS = /[\u0000-\u001f\u007f-\u009f\u202a-\u202e\u2066-\u2069]/u;
|
|
9
|
+
const XML_ENTITY = /&(amp|lt|gt|quot|apos);/gu;
|
|
10
|
+
const INVALID_XML_ENTITY = /&(?!(?:amp|lt|gt|quot|apos);)/u;
|
|
11
|
+
const PI_SKILL_BLOCK = /(?:^|\n) <skill>\n([\s\S]*?)\n <\/skill>(?=\n|$)/gu;
|
|
12
|
+
const PI_SKILL_FIELDS = /^ <name>([^\n]*)<\/name>\n <description>[\s\S]*<\/description>\n <location>([^\n]*)<\/location>$/u;
|
|
13
|
+
/** Parse the exact expansion emitted by Pi for `/skill:name [arguments]`. */
|
|
14
|
+
export function piStyleExplicitSkillObservations(request, limit) {
|
|
15
|
+
if (limit <= 0)
|
|
16
|
+
return [];
|
|
17
|
+
return trailingUserMessages(request)
|
|
18
|
+
.flatMap((message) => {
|
|
19
|
+
const textParts = message.content.filter((part) => part.type === "text");
|
|
20
|
+
if (textParts.length !== 1
|
|
21
|
+
|| message.content.some((part) => part.type !== "text" && part.type !== "image"))
|
|
22
|
+
return [];
|
|
23
|
+
const name = piStyleSkillName(textParts[0].text);
|
|
24
|
+
return name === undefined
|
|
25
|
+
? []
|
|
26
|
+
: [observation(name, "skill_use", "skill_command_expansion", null)];
|
|
27
|
+
})
|
|
28
|
+
.slice(0, limit);
|
|
29
|
+
}
|
|
30
|
+
/** Match a current model-emitted Pi `read` call to its exact skill catalog entry. */
|
|
31
|
+
export function piStyleAutomaticSkillObservations(request, output, limit) {
|
|
32
|
+
if (limit <= 0
|
|
33
|
+
|| request.sourceProtocol !== "openai_chat"
|
|
34
|
+
|| !request.tools.some((tool) => tool.kind === "function"
|
|
35
|
+
&& tool.name === "read"
|
|
36
|
+
&& tool.namespace === undefined)) {
|
|
37
|
+
return [];
|
|
38
|
+
}
|
|
39
|
+
const skillsByLocation = piStyleAvailableSkills(request);
|
|
40
|
+
if (skillsByLocation.size === 0)
|
|
41
|
+
return [];
|
|
42
|
+
const observedCapabilities = new Set();
|
|
43
|
+
const observed = [];
|
|
44
|
+
for (const item of output.items) {
|
|
45
|
+
const read = piSkillRead(item);
|
|
46
|
+
if (read === undefined)
|
|
47
|
+
continue;
|
|
48
|
+
const name = skillsByLocation.get(read.path);
|
|
49
|
+
if (name === undefined)
|
|
50
|
+
continue;
|
|
51
|
+
const skill = observation(name, "skill_load", "skill_file_read", read.toolCallId);
|
|
52
|
+
if (observedCapabilities.has(skill.capabilityId))
|
|
53
|
+
continue;
|
|
54
|
+
observedCapabilities.add(skill.capabilityId);
|
|
55
|
+
observed.push(skill);
|
|
56
|
+
if (observed.length === limit)
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
return observed;
|
|
60
|
+
}
|
|
61
|
+
function observation(name, eventType, evidenceSource, toolCallId) {
|
|
62
|
+
return {
|
|
63
|
+
eventType,
|
|
64
|
+
toolCallId,
|
|
65
|
+
capabilityId: `${CAPABILITY_PREFIX}${name}`,
|
|
66
|
+
observedName: name,
|
|
67
|
+
evidenceSource,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
function piStyleAvailableSkills(request) {
|
|
71
|
+
const skills = new Map();
|
|
72
|
+
const ambiguousLocations = new Set();
|
|
73
|
+
for (const item of request.items) {
|
|
74
|
+
if (item.type !== "message"
|
|
75
|
+
|| (item.role !== "system" && item.role !== "developer")) {
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
for (const part of item.content) {
|
|
79
|
+
if (part.type !== "text")
|
|
80
|
+
continue;
|
|
81
|
+
for (const section of part.text.matchAll(/(?:^|\n)<available_skills>\n([\s\S]*?)\n<\/available_skills>(?=\n|$)/gu)) {
|
|
82
|
+
const body = section[1];
|
|
83
|
+
if (body === undefined)
|
|
84
|
+
continue;
|
|
85
|
+
for (const block of body.matchAll(PI_SKILL_BLOCK)) {
|
|
86
|
+
const fields = block[1]?.match(PI_SKILL_FIELDS);
|
|
87
|
+
if (fields === null || fields === undefined)
|
|
88
|
+
continue;
|
|
89
|
+
const nameValue = decodePiXmlText(fields[1]);
|
|
90
|
+
const location = normalizedSkillPath(decodePiXmlText(fields[2]));
|
|
91
|
+
const name = nameValue === undefined
|
|
92
|
+
? undefined
|
|
93
|
+
: normalizedSkillName(nameValue);
|
|
94
|
+
if (name === undefined
|
|
95
|
+
|| location === undefined
|
|
96
|
+
|| ambiguousLocations.has(location)) {
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
const previous = skills.get(location);
|
|
100
|
+
if (previous !== undefined && previous !== name) {
|
|
101
|
+
skills.delete(location);
|
|
102
|
+
ambiguousLocations.add(location);
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
skills.set(location, name);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return skills;
|
|
112
|
+
}
|
|
113
|
+
function piSkillRead(item) {
|
|
114
|
+
if (item.type !== "tool_call"
|
|
115
|
+
|| item.kind !== "function"
|
|
116
|
+
|| item.namespace !== undefined
|
|
117
|
+
|| item.name !== "read") {
|
|
118
|
+
return undefined;
|
|
119
|
+
}
|
|
120
|
+
const input = interactionToolInput(item);
|
|
121
|
+
const path = input === undefined ? undefined : normalizedSkillPath(input.path);
|
|
122
|
+
return path === undefined ? undefined : { path, toolCallId: item.callId };
|
|
123
|
+
}
|
|
124
|
+
function decodePiXmlText(value) {
|
|
125
|
+
if (value === undefined || INVALID_XML_ENTITY.test(value))
|
|
126
|
+
return undefined;
|
|
127
|
+
const entities = {
|
|
128
|
+
amp: "&",
|
|
129
|
+
lt: "<",
|
|
130
|
+
gt: ">",
|
|
131
|
+
quot: '"',
|
|
132
|
+
apos: "'",
|
|
133
|
+
};
|
|
134
|
+
return value.replace(XML_ENTITY, (_entity, name) => entities[name] ?? "");
|
|
135
|
+
}
|
|
136
|
+
function normalizedSkillPath(value) {
|
|
137
|
+
if (typeof value !== "string")
|
|
138
|
+
return undefined;
|
|
139
|
+
return value.length > 0
|
|
140
|
+
&& value.length <= MAX_SKILL_PATH_CHARS
|
|
141
|
+
&& !UNSAFE_DISPLAY_CHARACTERS.test(value)
|
|
142
|
+
? value
|
|
143
|
+
: undefined;
|
|
144
|
+
}
|
|
145
|
+
function trailingUserMessages(request) {
|
|
146
|
+
let index = request.items.length - 1;
|
|
147
|
+
while (index >= 0) {
|
|
148
|
+
const item = request.items[index];
|
|
149
|
+
if (item.type === "message"
|
|
150
|
+
&& (item.role === "system" || item.role === "developer")) {
|
|
151
|
+
index -= 1;
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
break;
|
|
155
|
+
}
|
|
156
|
+
const messages = [];
|
|
157
|
+
while (index >= 0) {
|
|
158
|
+
const item = request.items[index];
|
|
159
|
+
if (item.type !== "message" || item.role !== "user")
|
|
160
|
+
break;
|
|
161
|
+
messages.push(item);
|
|
162
|
+
index -= 1;
|
|
163
|
+
}
|
|
164
|
+
return messages.reverse();
|
|
165
|
+
}
|
|
166
|
+
function piStyleSkillName(text) {
|
|
167
|
+
const namePrefix = '<skill name="';
|
|
168
|
+
if (!text.startsWith(namePrefix))
|
|
169
|
+
return undefined;
|
|
170
|
+
const nameEnd = text.indexOf('" location="', namePrefix.length);
|
|
171
|
+
if (nameEnd < 0)
|
|
172
|
+
return undefined;
|
|
173
|
+
const locationStart = nameEnd + '" location="'.length;
|
|
174
|
+
const locationEnd = text.indexOf('">\n', locationStart);
|
|
175
|
+
if (locationEnd < 0)
|
|
176
|
+
return undefined;
|
|
177
|
+
const bodyStart = locationEnd + '">\n'.length;
|
|
178
|
+
const bodyEnd = piStyleSkillBodyEnd(text, bodyStart);
|
|
179
|
+
if (bodyEnd < 0)
|
|
180
|
+
return undefined;
|
|
181
|
+
const name = normalizedSkillName(text.slice(namePrefix.length, nameEnd));
|
|
182
|
+
const location = text.slice(locationStart, locationEnd);
|
|
183
|
+
const body = text.slice(bodyStart, bodyEnd);
|
|
184
|
+
const referencesPrefix = "References are relative to ";
|
|
185
|
+
const referencesEnd = body.indexOf(".\n\n", referencesPrefix.length);
|
|
186
|
+
if (name === undefined
|
|
187
|
+
|| location.length === 0
|
|
188
|
+
|| location.length > MAX_SKILL_PATH_CHARS
|
|
189
|
+
|| UNSAFE_DISPLAY_CHARACTERS.test(location)
|
|
190
|
+
|| !body.startsWith(referencesPrefix)
|
|
191
|
+
|| referencesEnd < 0
|
|
192
|
+
|| body.slice(referencesEnd + ".\n\n".length).trim() === "") {
|
|
193
|
+
return undefined;
|
|
194
|
+
}
|
|
195
|
+
return name;
|
|
196
|
+
}
|
|
197
|
+
function piStyleSkillBodyEnd(text, bodyStart) {
|
|
198
|
+
let lastNonWhitespace = text.length - 1;
|
|
199
|
+
while (lastNonWhitespace >= 0 && /\s/u.test(text[lastNonWhitespace])) {
|
|
200
|
+
lastNonWhitespace -= 1;
|
|
201
|
+
}
|
|
202
|
+
let candidate = text.indexOf(PI_SKILL_CLOSE, bodyStart);
|
|
203
|
+
while (candidate >= 0) {
|
|
204
|
+
const suffixStart = candidate + PI_SKILL_CLOSE.length;
|
|
205
|
+
if (suffixStart === text.length
|
|
206
|
+
|| (text.startsWith("\n\n", suffixStart)
|
|
207
|
+
&& suffixStart + 2 <= lastNonWhitespace)) {
|
|
208
|
+
return candidate;
|
|
209
|
+
}
|
|
210
|
+
candidate = text.indexOf(PI_SKILL_CLOSE, suffixStart);
|
|
211
|
+
}
|
|
212
|
+
return -1;
|
|
213
|
+
}
|
|
214
|
+
function normalizedSkillName(value) {
|
|
215
|
+
const normalized = value.normalize("NFC").trim();
|
|
216
|
+
return normalized.length > 0
|
|
217
|
+
&& normalized.length <= MAX_SKILL_NAME_CHARS
|
|
218
|
+
&& normalized.length <= MAX_CAPABILITY_ID_CHARS - CAPABILITY_PREFIX.length
|
|
219
|
+
&& SKILL_NAME.test(normalized)
|
|
220
|
+
&& !UNSAFE_DISPLAY_CHARACTERS.test(normalized)
|
|
221
|
+
? normalized
|
|
222
|
+
: undefined;
|
|
223
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type NormalizedSkillPath, type PathPlatform } from "./paths.js";
|
|
2
|
+
export type ShellReadCommand = {
|
|
3
|
+
command: string;
|
|
4
|
+
workdir?: string;
|
|
5
|
+
};
|
|
6
|
+
/** Parse only direct, deterministic shell readers and return their resolved paths. */
|
|
7
|
+
export declare function readPathsFromShellCommand(input: ShellReadCommand, platform: PathPlatform, nesting?: number): NormalizedSkillPath[];
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import { normalizedAbsoluteSkillPath, resolvedSkillPath, } from "./paths.js";
|
|
2
|
+
import { executableCommand, restrictedShellSegments, trustedExecutableName, } from "./shell_words.js";
|
|
3
|
+
const MAX_SHELL_NESTING = 16;
|
|
4
|
+
const NO_VALUE_FLAGS = new Set();
|
|
5
|
+
const BAT_VALUE_FLAGS = new Set([
|
|
6
|
+
"--language",
|
|
7
|
+
"--line-range",
|
|
8
|
+
"--map-syntax",
|
|
9
|
+
"--style",
|
|
10
|
+
"--tabs",
|
|
11
|
+
"--terminal-width",
|
|
12
|
+
"--theme",
|
|
13
|
+
]);
|
|
14
|
+
const HEAD_TAIL_VALUE_FLAGS = new Set(["-c", "--bytes", "-n", "--lines"]);
|
|
15
|
+
const LESS_VALUE_FLAGS = new Set([
|
|
16
|
+
"--LOG-FILE",
|
|
17
|
+
"-j",
|
|
18
|
+
"--jump-target",
|
|
19
|
+
"--log-file",
|
|
20
|
+
"-o",
|
|
21
|
+
"-O",
|
|
22
|
+
"-p",
|
|
23
|
+
"--pattern",
|
|
24
|
+
"-P",
|
|
25
|
+
"--prompt",
|
|
26
|
+
"--shift",
|
|
27
|
+
"--tabs",
|
|
28
|
+
"-x",
|
|
29
|
+
"-y",
|
|
30
|
+
"-z",
|
|
31
|
+
]);
|
|
32
|
+
const NL_VALUE_FLAGS = new Set([
|
|
33
|
+
"--body-numbering",
|
|
34
|
+
"--footer-numbering",
|
|
35
|
+
"--header-numbering",
|
|
36
|
+
"--join-blank-lines",
|
|
37
|
+
"--number-format",
|
|
38
|
+
"--number-separator",
|
|
39
|
+
"--number-width",
|
|
40
|
+
"--section-delimiter",
|
|
41
|
+
"--starting-line-number",
|
|
42
|
+
"-b",
|
|
43
|
+
"-d",
|
|
44
|
+
"-f",
|
|
45
|
+
"-h",
|
|
46
|
+
"-i",
|
|
47
|
+
"-n",
|
|
48
|
+
"-s",
|
|
49
|
+
"-v",
|
|
50
|
+
"-w",
|
|
51
|
+
]);
|
|
52
|
+
const AWK_VALUE_FLAGS = new Set(["-F", "-v"]);
|
|
53
|
+
/** Parse only direct, deterministic shell readers and return their resolved paths. */
|
|
54
|
+
export function readPathsFromShellCommand(input, platform, nesting = 0) {
|
|
55
|
+
if (nesting > MAX_SHELL_NESTING)
|
|
56
|
+
return [];
|
|
57
|
+
const segments = restrictedShellSegments(input.command, platform);
|
|
58
|
+
if (segments === undefined)
|
|
59
|
+
return [];
|
|
60
|
+
const normalizedWorkdir = normalizedAbsoluteSkillPath(input.workdir);
|
|
61
|
+
let workdir = normalizedWorkdir?.platform === platform
|
|
62
|
+
? normalizedWorkdir
|
|
63
|
+
: undefined;
|
|
64
|
+
const observed = [];
|
|
65
|
+
let previousOperator = null;
|
|
66
|
+
let previousWasSupported = false;
|
|
67
|
+
for (const segment of segments) {
|
|
68
|
+
const command = executableCommand(segment.words);
|
|
69
|
+
if (command === undefined) {
|
|
70
|
+
previousOperator = segment.next;
|
|
71
|
+
previousWasSupported = false;
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
const executable = trustedExecutableName(command.executable, platform);
|
|
75
|
+
if (executable === undefined) {
|
|
76
|
+
previousOperator = segment.next;
|
|
77
|
+
previousWasSupported = false;
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
if (previousOperator === "||")
|
|
81
|
+
return [];
|
|
82
|
+
if (previousOperator === "&&" && !previousWasSupported) {
|
|
83
|
+
previousOperator = segment.next;
|
|
84
|
+
previousWasSupported = false;
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
if (executable === "cd") {
|
|
88
|
+
const nextWorkdir = cdTarget(command.args, workdir, platform);
|
|
89
|
+
if (nextWorkdir !== undefined
|
|
90
|
+
&& (segment.next === "&&" || segment.next === ";" || segment.next === "\n"))
|
|
91
|
+
workdir = nextWorkdir;
|
|
92
|
+
previousOperator = segment.next;
|
|
93
|
+
previousWasSupported = nextWorkdir !== undefined;
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
if (platform === "posix"
|
|
97
|
+
&& (executable === "bash" || executable === "zsh" || executable === "sh")
|
|
98
|
+
&& command.args.length >= 2
|
|
99
|
+
&& /^-[^-]*c[^-]*$/u.test(command.args[0])) {
|
|
100
|
+
observed.push(...readPathsFromShellCommand({
|
|
101
|
+
command: command.args[1],
|
|
102
|
+
...(workdir === undefined ? {} : { workdir: workdir.value }),
|
|
103
|
+
}, platform, nesting + 1));
|
|
104
|
+
previousOperator = segment.next;
|
|
105
|
+
previousWasSupported = false;
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
const paths = readerPaths(executable, command.args, platform);
|
|
109
|
+
if (paths === undefined) {
|
|
110
|
+
previousOperator = segment.next;
|
|
111
|
+
previousWasSupported = false;
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
for (const path of paths) {
|
|
115
|
+
const resolved = resolvedSkillPath(path, workdir);
|
|
116
|
+
if (resolved?.platform === platform)
|
|
117
|
+
observed.push(resolved);
|
|
118
|
+
}
|
|
119
|
+
previousOperator = segment.next;
|
|
120
|
+
previousWasSupported = true;
|
|
121
|
+
}
|
|
122
|
+
return observed;
|
|
123
|
+
}
|
|
124
|
+
function readerPaths(executable, args, platform) {
|
|
125
|
+
switch (executable) {
|
|
126
|
+
case "cat":
|
|
127
|
+
case "more":
|
|
128
|
+
return ordinaryFileOperands(args, NO_VALUE_FLAGS);
|
|
129
|
+
case "bat":
|
|
130
|
+
case "batcat":
|
|
131
|
+
return ordinaryFileOperands(args, BAT_VALUE_FLAGS);
|
|
132
|
+
case "head":
|
|
133
|
+
case "tail":
|
|
134
|
+
return ordinaryFileOperands(args, HEAD_TAIL_VALUE_FLAGS);
|
|
135
|
+
case "less":
|
|
136
|
+
return ordinaryFileOperands(args, LESS_VALUE_FLAGS);
|
|
137
|
+
case "nl":
|
|
138
|
+
return ordinaryFileOperands(args, NL_VALUE_FLAGS);
|
|
139
|
+
case "sed":
|
|
140
|
+
return sedFileOperands(args);
|
|
141
|
+
case "awk":
|
|
142
|
+
return ordinaryFileOperands(args, AWK_VALUE_FLAGS).slice(1);
|
|
143
|
+
case "get-content":
|
|
144
|
+
case "gc":
|
|
145
|
+
case "type":
|
|
146
|
+
return platform === "windows" ? powershellFileOperands(args) : [];
|
|
147
|
+
default:
|
|
148
|
+
return undefined;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
function ordinaryFileOperands(args, valueFlags) {
|
|
152
|
+
const files = [];
|
|
153
|
+
let optionsEnded = false;
|
|
154
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
155
|
+
const argument = args[index];
|
|
156
|
+
if (!optionsEnded && argument === "--") {
|
|
157
|
+
optionsEnded = true;
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
if (!optionsEnded && valueFlags.has(argument)) {
|
|
161
|
+
index += 1;
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
if (!optionsEnded && argument.startsWith("-"))
|
|
165
|
+
continue;
|
|
166
|
+
files.push(argument);
|
|
167
|
+
}
|
|
168
|
+
return files;
|
|
169
|
+
}
|
|
170
|
+
function sedFileOperands(args) {
|
|
171
|
+
const files = [];
|
|
172
|
+
let explicitProgram = false;
|
|
173
|
+
let implicitProgramConsumed = false;
|
|
174
|
+
let optionsEnded = false;
|
|
175
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
176
|
+
const argument = args[index];
|
|
177
|
+
if (!optionsEnded && argument === "--") {
|
|
178
|
+
optionsEnded = true;
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
if (!optionsEnded && ["-e", "--expression", "-f", "--file"].includes(argument)) {
|
|
182
|
+
explicitProgram = true;
|
|
183
|
+
index += 1;
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
if (!optionsEnded && (argument.startsWith("-e")
|
|
187
|
+
|| argument.startsWith("--expression=")
|
|
188
|
+
|| argument.startsWith("-f")
|
|
189
|
+
|| argument.startsWith("--file="))) {
|
|
190
|
+
explicitProgram = true;
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
if (!optionsEnded && argument.startsWith("-"))
|
|
194
|
+
continue;
|
|
195
|
+
if (!explicitProgram && !implicitProgramConsumed) {
|
|
196
|
+
implicitProgramConsumed = true;
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
files.push(argument);
|
|
200
|
+
}
|
|
201
|
+
return files;
|
|
202
|
+
}
|
|
203
|
+
function powershellFileOperands(args) {
|
|
204
|
+
const files = [];
|
|
205
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
206
|
+
const argument = args[index];
|
|
207
|
+
if (/^-(?:Path|LiteralPath)$/iu.test(argument)) {
|
|
208
|
+
const path = args[index + 1];
|
|
209
|
+
if (path !== undefined)
|
|
210
|
+
files.push(path);
|
|
211
|
+
index += 1;
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
if (!argument.startsWith("-"))
|
|
215
|
+
files.push(argument);
|
|
216
|
+
}
|
|
217
|
+
return files;
|
|
218
|
+
}
|
|
219
|
+
function cdTarget(args, workdir, platform) {
|
|
220
|
+
const operands = args.filter((argument) => argument !== "--" && !argument.startsWith("-"));
|
|
221
|
+
const target = operands.at(-1);
|
|
222
|
+
const resolved = target === undefined
|
|
223
|
+
? undefined
|
|
224
|
+
: resolvedSkillPath(target, workdir);
|
|
225
|
+
return resolved?.platform === platform ? resolved : undefined;
|
|
226
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { PathPlatform } from "./paths.js";
|
|
2
|
+
export type ShellOperator = "&" | "&&" | "|" | "||" | ";" | "\n";
|
|
3
|
+
export type ShellSegment = {
|
|
4
|
+
words: string[];
|
|
5
|
+
next: ShellOperator | null;
|
|
6
|
+
};
|
|
7
|
+
export type ExecutableCommand = {
|
|
8
|
+
executable: string;
|
|
9
|
+
args: string[];
|
|
10
|
+
};
|
|
11
|
+
export declare function executableCommand(words: string[]): ExecutableCommand | undefined;
|
|
12
|
+
export declare function restrictedShellSegments(command: string, platform: PathPlatform): ShellSegment[] | undefined;
|
|
13
|
+
export declare function trustedExecutableName(command: string, platform: PathPlatform): string | undefined;
|